Move identity key verification into libaxolotol. With tests.

This commit is contained in:
Moxie Marlinspike 2014-04-28 11:46:37 -07:00
parent 81ae9af2e4
commit 931605a1c4
13 changed files with 266 additions and 145 deletions

View file

@ -44,4 +44,10 @@ public class InMemoryIdentityKeyStore implements IdentityKeyStore {
public void saveIdentity(long recipientId, IdentityKey identityKey) { public void saveIdentity(long recipientId, IdentityKey identityKey) {
trustedKeys.put(recipientId, identityKey); trustedKeys.put(recipientId, identityKey);
} }
@Override
public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey) {
IdentityKey trusted = trustedKeys.get(recipientId);
return (trusted == null || trusted.equals(identityKey));
}
} }

View file

@ -11,6 +11,8 @@ import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.SessionBuilder; import org.whispersystems.libaxolotl.SessionBuilder;
import org.whispersystems.libaxolotl.SessionCipher; import org.whispersystems.libaxolotl.SessionCipher;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.ecc.Curve; import org.whispersystems.libaxolotl.ecc.Curve;
import org.whispersystems.libaxolotl.ecc.ECKeyPair; import org.whispersystems.libaxolotl.ecc.ECKeyPair;
import org.whispersystems.libaxolotl.ecc.ECPublicKey; import org.whispersystems.libaxolotl.ecc.ECPublicKey;
@ -33,8 +35,7 @@ public class SessionBuilderTest extends AndroidTestCase {
private static final long BOB_RECIPIENT_ID = 2L; private static final long BOB_RECIPIENT_ID = 2L;
public void testBasicPreKey() public void testBasicPreKey()
throws InvalidKeyException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException throws InvalidKeyException, InvalidVersionException, InvalidMessageException, InvalidKeyIdException, DuplicateMessageException, LegacyMessageException, UntrustedIdentityException {
{
SessionStore aliceSessionStore = new InMemorySessionStore(); SessionStore aliceSessionStore = new InMemorySessionStore();
PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore();
IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore(); IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore();
@ -74,9 +75,55 @@ public class SessionBuilderTest extends AndroidTestCase {
byte[] plaintext = bobSessionCipher.decrypt(incomingMessage.getWhisperMessage().serialize()); byte[] plaintext = bobSessionCipher.decrypt(incomingMessage.getWhisperMessage().serialize());
assertTrue(originalMessage.equals(new String(plaintext))); assertTrue(originalMessage.equals(new String(plaintext)));
CiphertextMessage bobOutgoingMessage = bobSessionCipher.encrypt(originalMessage.getBytes());
assertTrue(bobOutgoingMessage.getType() == CiphertextMessage.WHISPER_TYPE);
byte[] alicePlaintext = aliceSessionCipher.decrypt(bobOutgoingMessage.serialize());
assertTrue(new String(alicePlaintext).equals(originalMessage));
runInteraction(aliceSessionStore, bobSessionStore);
aliceSessionStore = new InMemorySessionStore();
aliceIdentityKeyStore = new InMemoryIdentityKeyStore();
aliceSessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore,
aliceIdentityKeyStore,
BOB_RECIPIENT_ID, 1);
aliceSessionCipher = new SessionCipher(aliceSessionStore, BOB_RECIPIENT_ID, 1);
bobPreKey = new InMemoryPreKey(31338, Curve.generateKeyPair(true),
bobIdentityKeyStore.getIdentityKeyPair().getPublicKey(),
bobIdentityKeyStore.getLocalRegistrationId());
bobPreKeyStore.store(31338, bobPreKey);
aliceSessionBuilder.process(bobPreKey);
outgoingMessage = aliceSessionCipher.encrypt(originalMessage.getBytes());
try {
bobSessionBuilder.process(new PreKeyWhisperMessage(outgoingMessage.serialize()));
throw new AssertionError("shouldn't be trusted!");
} catch (UntrustedIdentityException uie) {
bobIdentityKeyStore.saveIdentity(ALICE_RECIPIENT_ID, new PreKeyWhisperMessage(outgoingMessage.serialize()).getIdentityKey());
bobSessionBuilder.process(new PreKeyWhisperMessage(outgoingMessage.serialize()));
} }
public void testBasicKeyExchange() throws InvalidKeyException, LegacyMessageException, InvalidMessageException, DuplicateMessageException { plaintext = bobSessionCipher.decrypt(new PreKeyWhisperMessage(outgoingMessage.serialize()).getWhisperMessage().serialize());
assertTrue(new String(plaintext).equals(originalMessage));
bobPreKey = new InMemoryPreKey(31337, Curve.generateKeyPair(true),
aliceIdentityKeyStore.getIdentityKeyPair().getPublicKey(),
bobIdentityKeyStore.getLocalRegistrationId());
try {
aliceSessionBuilder.process(bobPreKey);
throw new AssertionError("shoulnd't be trusted!");
} catch (UntrustedIdentityException uie) {
// good
}
}
public void testBasicKeyExchange() throws InvalidKeyException, LegacyMessageException, InvalidMessageException, DuplicateMessageException, UntrustedIdentityException, StaleKeyExchangeException {
SessionStore aliceSessionStore = new InMemorySessionStore(); SessionStore aliceSessionStore = new InMemorySessionStore();
PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore();
IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore(); IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore();
@ -104,11 +151,28 @@ public class SessionBuilderTest extends AndroidTestCase {
assertTrue(bobSessionStore.contains(ALICE_RECIPIENT_ID, 1)); assertTrue(bobSessionStore.contains(ALICE_RECIPIENT_ID, 1));
runInteraction(aliceSessionStore, bobSessionStore); runInteraction(aliceSessionStore, bobSessionStore);
aliceSessionStore = new InMemorySessionStore();
aliceIdentityKeyStore = new InMemoryIdentityKeyStore();
aliceSessionBuilder = new SessionBuilder(aliceSessionStore, alicePreKeyStore,
aliceIdentityKeyStore, BOB_RECIPIENT_ID, 1);
aliceKeyExchangeMessage = aliceSessionBuilder.process();
try {
bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage);
throw new AssertionError("This identity shouldn't be trusted!");
} catch (UntrustedIdentityException uie) {
bobIdentityKeyStore.saveIdentity(ALICE_RECIPIENT_ID, aliceKeyExchangeMessage.getIdentityKey());
bobKeyExchangeMessage = bobSessionBuilder.process(aliceKeyExchangeMessage);
}
assertTrue(aliceSessionBuilder.process(bobKeyExchangeMessage) == null);
runInteraction(aliceSessionStore, bobSessionStore);
} }
public void testSimultaneousKeyExchange() public void testSimultaneousKeyExchange()
throws InvalidKeyException, DuplicateMessageException, LegacyMessageException, InvalidMessageException throws InvalidKeyException, DuplicateMessageException, LegacyMessageException, InvalidMessageException, UntrustedIdentityException, StaleKeyExchangeException {
{
SessionStore aliceSessionStore = new InMemorySessionStore(); SessionStore aliceSessionStore = new InMemorySessionStore();
PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore(); PreKeyStore alicePreKeyStore = new InMemoryPreKeyStore();
IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore(); IdentityKeyStore aliceIdentityKeyStore = new InMemoryIdentityKeyStore();

View file

@ -13,6 +13,7 @@ import org.whispersystems.libaxolotl.state.PreKey;
import org.whispersystems.libaxolotl.state.PreKeyRecord; import org.whispersystems.libaxolotl.state.PreKeyRecord;
import org.whispersystems.libaxolotl.state.PreKeyStore; import org.whispersystems.libaxolotl.state.PreKeyStore;
import org.whispersystems.libaxolotl.state.SessionRecord; import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionState;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.libaxolotl.util.KeyHelper; import org.whispersystems.libaxolotl.util.KeyHelper;
import org.whispersystems.libaxolotl.util.Medium; import org.whispersystems.libaxolotl.util.Medium;
@ -77,16 +78,19 @@ public class SessionBuilder {
* that corresponds to the PreKey ID in * that corresponds to the PreKey ID in
* the message. * the message.
* @throws org.whispersystems.libaxolotl.InvalidKeyException when the message is formatted incorrectly. * @throws org.whispersystems.libaxolotl.InvalidKeyException when the message is formatted incorrectly.
* @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the {@link IdentityKey} of the sender is untrusted.
*/ */
public void process(PreKeyWhisperMessage message) public void process(PreKeyWhisperMessage message)
throws InvalidKeyIdException, InvalidKeyException throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException
{ {
int preKeyId = message.getPreKeyId(); int preKeyId = message.getPreKeyId();
ECPublicKey theirBaseKey = message.getBaseKey(); ECPublicKey theirBaseKey = message.getBaseKey();
ECPublicKey theirEphemeralKey = message.getWhisperMessage().getSenderEphemeral(); ECPublicKey theirEphemeralKey = message.getWhisperMessage().getSenderEphemeral();
IdentityKey theirIdentityKey = message.getIdentityKey(); IdentityKey theirIdentityKey = message.getIdentityKey();
Log.w(TAG, "Received pre-key with local key ID: " + preKeyId); if (!identityKeyStore.isTrustedIdentity(recipientId, theirIdentityKey)) {
throw new UntrustedIdentityException();
}
if (!preKeyStore.contains(preKeyId) && if (!preKeyStore.contains(preKeyId) &&
sessionStore.contains(recipientId, deviceId)) sessionStore.contains(recipientId, deviceId))
@ -134,8 +138,16 @@ public class SessionBuilder {
* @param preKey A PreKey for the destination recipient, retrieved from a server. * @param preKey A PreKey for the destination recipient, retrieved from a server.
* @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKey} is * @throws InvalidKeyException when the {@link org.whispersystems.libaxolotl.state.PreKey} is
* badly formatted. * badly formatted.
* @throws org.whispersystems.libaxolotl.UntrustedIdentityException when the sender's
* {@link IdentityKey} is not
* trusted.
*/ */
public void process(PreKey preKey) throws InvalidKeyException { public void process(PreKey preKey) throws InvalidKeyException, UntrustedIdentityException {
if (!identityKeyStore.isTrustedIdentity(recipientId, preKey.getIdentityKey())) {
throw new UntrustedIdentityException();
}
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId); SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
ECKeyPair ourBaseKey = Curve.generateKeyPair(true); ECKeyPair ourBaseKey = Curve.generateKeyPair(true);
ECKeyPair ourEphemeralKey = Curve.generateKeyPair(true); ECKeyPair ourEphemeralKey = Curve.generateKeyPair(true);
@ -168,43 +180,34 @@ public class SessionBuilder {
* @return The KeyExchangeMessage to respond with, or null if no response is necessary. * @return The KeyExchangeMessage to respond with, or null if no response is necessary.
* @throws InvalidKeyException if the received KeyExchangeMessage is badly formatted. * @throws InvalidKeyException if the received KeyExchangeMessage is badly formatted.
*/ */
public KeyExchangeMessage process(KeyExchangeMessage message) throws InvalidKeyException { public KeyExchangeMessage process(KeyExchangeMessage message)
throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException
{
if (!identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey())) {
throw new UntrustedIdentityException();
}
KeyExchangeMessage responseMessage = null; KeyExchangeMessage responseMessage = null;
SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId); SessionRecord sessionRecord = sessionStore.load(recipientId, deviceId);
Log.w(TAG, "Received key exchange with sequence: " + message.getSequence()); Log.w(TAG, "Received key exchange with sequence: " + message.getSequence());
if (message.isInitiate()) { if (message.isInitiate()) {
ECKeyPair ourBaseKey, ourEphemeralKey;
IdentityKeyPair ourIdentityKey;
int flags = KeyExchangeMessage.RESPONSE_FLAG;
Log.w(TAG, "KeyExchange is an initiate."); Log.w(TAG, "KeyExchange is an initiate.");
responseMessage = processInitiate(sessionRecord, message);
if (!sessionRecord.getSessionState().hasPendingKeyExchange()) {
Log.w(TAG, "We don't have a pending initiate...");
ourBaseKey = Curve.generateKeyPair(true);
ourEphemeralKey = Curve.generateKeyPair(true);
ourIdentityKey = identityKeyStore.getIdentityKeyPair();
sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey,
ourEphemeralKey, ourIdentityKey);
} else {
Log.w(TAG, "We already 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);
} }
responseMessage = new KeyExchangeMessage(message.getSequence(), if (message.isResponse()) {
flags, ourBaseKey.getPublicKey(), SessionState sessionState = sessionRecord.getSessionState();
ourEphemeralKey.getPublicKey(), boolean hasPendingKeyExchange = sessionState.hasPendingKeyExchange();
ourIdentityKey.getPublicKey()); boolean isSimultaneousInitiateResponse = message.isResponseForSimultaneousInitiate();
if ((!hasPendingKeyExchange || sessionState.getPendingKeyExchangeSequence() != message.getSequence()) &&
!isSimultaneousInitiateResponse)
{
throw new StaleKeyExchangeException();
}
} }
if (message.getSequence() != sessionRecord.getSessionState().getPendingKeyExchangeSequence()) { if (message.getSequence() != sessionRecord.getSessionState().getPendingKeyExchangeSequence()) {
@ -232,6 +235,39 @@ public class SessionBuilder {
return responseMessage; return responseMessage;
} }
private KeyExchangeMessage processInitiate(SessionRecord sessionRecord, KeyExchangeMessage message)
throws InvalidKeyException
{
ECKeyPair ourBaseKey, ourEphemeralKey;
IdentityKeyPair ourIdentityKey;
int flags = KeyExchangeMessage.RESPONSE_FLAG;
if (!sessionRecord.getSessionState().hasPendingKeyExchange()) {
Log.w(TAG, "We don't have a pending initiate...");
ourBaseKey = Curve.generateKeyPair(true);
ourEphemeralKey = Curve.generateKeyPair(true);
ourIdentityKey = identityKeyStore.getIdentityKeyPair();
sessionRecord.getSessionState().setPendingKeyExchange(message.getSequence(), ourBaseKey,
ourEphemeralKey, ourIdentityKey);
} else {
Log.w(TAG, "We already 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);
}
return new KeyExchangeMessage(message.getSequence(),
flags, ourBaseKey.getPublicKey(),
ourEphemeralKey.getPublicKey(),
ourIdentityKey.getPublicKey());
}
/** /**
* Initiate a new session by sending an initial KeyExchangeMessage to the recipient. * Initiate a new session by sending an initial KeyExchangeMessage to the recipient.
* *

View file

@ -0,0 +1,4 @@
package org.whispersystems.libaxolotl;
public class StaleKeyExchangeException extends Throwable {
}

View file

@ -0,0 +1,4 @@
package org.whispersystems.libaxolotl;
public class UntrustedIdentityException extends Exception {
}

View file

@ -37,4 +37,21 @@ public interface IdentityKeyStore {
*/ */
public void saveIdentity(long recipientId, IdentityKey identityKey); public void saveIdentity(long recipientId, IdentityKey identityKey);
/**
* Verify a remote client's identity key.
* <p>
* Determine whether a remote client's identity is trusted. Convention is
* that the TextSecure protocol is 'trust on first use.' This means that
* an identity key is considered 'trusted' if there is no entry for the recipient
* in the local store, or if it matches the saved key for a recipient in the local
* store. Only if it mismatches an entry in the local store is it considered
* 'untrusted.'
*
* @param recipientId The recipient ID of the remote client.
* @param identityKey The identity key to verify.
* @return true if trusted, false if untrusted.
*/
public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey);
} }

View file

@ -29,9 +29,11 @@ import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.Button; import android.widget.Button;
import android.widget.TextView; import android.widget.TextView;
import android.widget.Toast;
import org.thoughtcrime.securesms.crypto.DecryptingQueue; import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor; import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.TextSecureIdentityKeyStore;
import org.thoughtcrime.securesms.database.DatabaseFactory; import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.SendReceiveService; import org.thoughtcrime.securesms.service.SendReceiveService;
@ -43,9 +45,12 @@ import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.CiphertextMessage; import org.whispersystems.libaxolotl.protocol.CiphertextMessage;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.state.IdentityKeyStore;
import org.whispersystems.textsecure.crypto.IdentityKeyParcelable; import org.whispersystems.textsecure.crypto.IdentityKeyParcelable;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.IncomingPushMessage; import org.whispersystems.textsecure.push.IncomingPushMessage;
@ -91,14 +96,8 @@ public class ReceiveKeyActivity extends Activity {
try { try {
initializeKey(); initializeKey();
initializeText(); initializeText();
} catch (InvalidKeyException ike) { } catch (InvalidKeyException | InvalidVersionException | InvalidMessageException | LegacyMessageException ike) {
Log.w("ReceiveKeyActivity", ike); Log.w("ReceiveKeyActivity", ike);
} catch (InvalidVersionException ive) {
Log.w("ReceiveKeyActivity", ive);
} catch (InvalidMessageException e) {
Log.w("ReceiveKeyActivity", e);
} catch (LegacyMessageException e) {
Log.w("ReceiveKeyActivity", e);
} }
initializeListeners(); initializeListeners();
} }
@ -147,12 +146,12 @@ public class ReceiveKeyActivity extends Activity {
} }
private boolean isTrusted(KeyExchangeMessage message, PreKeyWhisperMessage messageBundle, IdentityKey identityUpdateMessage) { private boolean isTrusted(KeyExchangeMessage message, PreKeyWhisperMessage messageBundle, IdentityKey identityUpdateMessage) {
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), recipientDeviceId); long recipientId = recipient.getRecipientId();
KeyExchangeProcessor processor = new KeyExchangeProcessor(this, masterSecret, recipientDevice); IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(this, masterSecret);
if (message != null) return processor.isTrusted(message); if (message != null) return identityKeyStore.isTrustedIdentity(recipientId, message.getIdentityKey());
else if (messageBundle != null) return processor.isTrusted(messageBundle); else if (messageBundle != null) return identityKeyStore.isTrustedIdentity(recipientId, messageBundle.getIdentityKey());
else if (identityUpdateMessage != null) return processor.isTrusted(identityUpdateMessage); else if (identityUpdateMessage != null) return identityKeyStore.isTrustedIdentity(recipientId, identityUpdateMessage);
return false; return false;
} }
@ -224,6 +223,10 @@ public class ReceiveKeyActivity extends Activity {
KeyExchangeProcessor processor = new KeyExchangeProcessor(ReceiveKeyActivity.this, KeyExchangeProcessor processor = new KeyExchangeProcessor(ReceiveKeyActivity.this,
masterSecret, recipientDevice); masterSecret, recipientDevice);
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(ReceiveKeyActivity.this,
masterSecret);
identityKeyStore.saveIdentity(recipient.getRecipientId(), keyExchangeMessage.getIdentityKey());
processor.processKeyExchangeMessage(keyExchangeMessage, threadId); processor.processKeyExchangeMessage(keyExchangeMessage, threadId);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
@ -232,12 +235,21 @@ public class ReceiveKeyActivity extends Activity {
Log.w("ReceiveKeyActivity", e); Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsCorruptKeyExchange(messageId); .markAsCorruptKeyExchange(messageId);
} catch (StaleKeyExchangeException e) {
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsStaleKeyExchange(messageId);
} catch (UntrustedIdentityException e) {
throw new AssertionError(e);
} }
} else if (keyExchangeMessageBundle != null) { } else if (keyExchangeMessageBundle != null) {
try { try {
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), recipientDeviceId); RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), recipientDeviceId);
KeyExchangeProcessor processor = new KeyExchangeProcessor(ReceiveKeyActivity.this, KeyExchangeProcessor processor = new KeyExchangeProcessor(ReceiveKeyActivity.this,
masterSecret, recipientDevice); masterSecret, recipientDevice);
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(ReceiveKeyActivity.this,
masterSecret);
identityKeyStore.saveIdentity(recipient.getRecipientId(), keyExchangeMessageBundle.getIdentityKey());
processor.processKeyExchangeMessage(keyExchangeMessageBundle); processor.processKeyExchangeMessage(keyExchangeMessageBundle);
CiphertextMessage bundledMessage = keyExchangeMessageBundle.getWhisperMessage(); CiphertextMessage bundledMessage = keyExchangeMessageBundle.getWhisperMessage();
@ -264,18 +276,13 @@ public class ReceiveKeyActivity extends Activity {
threadId, recipient.getNumber(), recipientDeviceId, threadId, recipient.getNumber(), recipientDeviceId,
messageBody, true, false, false); messageBody, true, false, false);
} }
} catch (InvalidKeyIdException e) { } catch (InvalidKeyIdException | InvalidNumberException | InvalidKeyException e) {
Log.w("ReceiveKeyActivity", e); Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsCorruptKeyExchange(messageId); .markAsCorruptKeyExchange(messageId);
} catch (InvalidKeyException e) { } catch (UntrustedIdentityException e) {
Log.w("ReceiveKeyActivity", e); Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this) Toast.makeText(ReceiveKeyActivity.this, "Untrusted!", Toast.LENGTH_LONG).show();
.markAsCorruptKeyExchange(messageId);
} catch (InvalidNumberException e) {
Log.w("ReceiveKeyActivity", e);
DatabaseFactory.getEncryptingSmsDatabase(ReceiveKeyActivity.this)
.markAsCorruptKeyExchange(messageId);
} }
} else if (identityUpdateMessage != null) { } else if (identityUpdateMessage != null) {
DatabaseFactory.getIdentityDatabase(ReceiveKeyActivity.this) DatabaseFactory.getIdentityDatabase(ReceiveKeyActivity.this)

View file

@ -45,6 +45,8 @@ import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.SessionCipher; import org.whispersystems.libaxolotl.SessionCipher;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage; import org.whispersystems.libaxolotl.protocol.WhisperMessage;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
@ -433,12 +435,8 @@ public class DecryptingQueue {
KeyExchangeMessage message = new KeyExchangeMessage(Base64.decodeWithoutPadding(plaintextBody)); KeyExchangeMessage message = new KeyExchangeMessage(Base64.decodeWithoutPadding(plaintextBody));
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
if (processor.isStale(message)) {
DatabaseFactory.getEncryptingSmsDatabase(context).markAsStaleKeyExchange(messageId);
} else if (processor.isTrusted(message)) {
DatabaseFactory.getEncryptingSmsDatabase(context).markAsProcessedKeyExchange(messageId);
processor.processKeyExchangeMessage(message, threadId); processor.processKeyExchangeMessage(message, threadId);
} DatabaseFactory.getEncryptingSmsDatabase(context).markAsProcessedKeyExchange(messageId);
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
Log.w("DecryptingQueue", e); Log.w("DecryptingQueue", e);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageId); DatabaseFactory.getEncryptingSmsDatabase(context).markAsInvalidVersionKeyExchange(messageId);
@ -448,6 +446,11 @@ public class DecryptingQueue {
} catch (LegacyMessageException e) { } catch (LegacyMessageException e) {
Log.w("DecryptingQueue", e); Log.w("DecryptingQueue", e);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsLegacyVersion(messageId); DatabaseFactory.getEncryptingSmsDatabase(context).markAsLegacyVersion(messageId);
} catch (StaleKeyExchangeException e) {
Log.w("DecryptingQueue", e);
DatabaseFactory.getEncryptingSmsDatabase(context).markAsStaleKeyExchange(messageId);
} catch (UntrustedIdentityException e) {
Log.w("DecryptingQueue", e);
} }
} }
} }

View file

@ -3,22 +3,20 @@ package org.thoughtcrime.securesms.crypto;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.service.KeyCachingService; import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.PreKeyService; import org.thoughtcrime.securesms.service.PreKeyService;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage; import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.whispersystems.libaxolotl.IdentityKey;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidKeyIdException; import org.whispersystems.libaxolotl.InvalidKeyIdException;
import org.whispersystems.libaxolotl.SessionBuilder; import org.whispersystems.libaxolotl.SessionBuilder;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.state.IdentityKeyStore; import org.whispersystems.libaxolotl.state.IdentityKeyStore;
import org.whispersystems.libaxolotl.state.PreKeyStore; import org.whispersystems.libaxolotl.state.PreKeyStore;
import org.whispersystems.libaxolotl.state.SessionRecord;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.push.PreKeyEntity; import org.whispersystems.textsecure.push.PreKeyEntity;
@ -41,7 +39,6 @@ public class KeyExchangeProcessor {
private RecipientDevice recipientDevice; private RecipientDevice recipientDevice;
private MasterSecret masterSecret; private MasterSecret masterSecret;
private SessionBuilder sessionBuilder; private SessionBuilder sessionBuilder;
private SessionStore sessionStore;
public KeyExchangeProcessor(Context context, MasterSecret masterSecret, RecipientDevice recipientDevice) public KeyExchangeProcessor(Context context, MasterSecret masterSecret, RecipientDevice recipientDevice)
{ {
@ -51,51 +48,22 @@ public class KeyExchangeProcessor {
IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret); IdentityKeyStore identityKeyStore = new TextSecureIdentityKeyStore(context, masterSecret);
PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret); PreKeyStore preKeyStore = new TextSecurePreKeyStore(context, masterSecret);
SessionStore sessionStore = new TextSecureSessionStore(context, masterSecret);
this.sessionStore = new TextSecureSessionStore(context, masterSecret);
this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, identityKeyStore, this.sessionBuilder = new SessionBuilder(sessionStore, preKeyStore, identityKeyStore,
recipientDevice.getRecipientId(), recipientDevice.getRecipientId(),
recipientDevice.getDeviceId()); recipientDevice.getDeviceId());
} }
public boolean isTrusted(PreKeyWhisperMessage message) {
return isTrusted(message.getIdentityKey());
}
public boolean isTrusted(PreKeyEntity entity) {
return isTrusted(entity.getIdentityKey());
}
public boolean isTrusted(KeyExchangeMessage message) {
return message.hasIdentityKey() && isTrusted(message.getIdentityKey());
}
public boolean isTrusted(IdentityKey identityKey) {
return DatabaseFactory.getIdentityDatabase(context).isValidIdentity(masterSecret,
recipientDevice.getRecipientId(),
identityKey);
}
public boolean isStale(KeyExchangeMessage message) {
SessionRecord sessionRecord = sessionStore.load(recipientDevice.getRecipientId(),
recipientDevice.getDeviceId());
return
message.isResponse() &&
(!sessionRecord.getSessionState().hasPendingKeyExchange() ||
sessionRecord.getSessionState().getPendingKeyExchangeSequence() != message.getSequence()) &&
!message.isResponseForSimultaneousInitiate();
}
public void processKeyExchangeMessage(PreKeyWhisperMessage message) public void processKeyExchangeMessage(PreKeyWhisperMessage message)
throws InvalidKeyIdException, InvalidKeyException throws InvalidKeyIdException, InvalidKeyException, UntrustedIdentityException
{ {
sessionBuilder.process(message); sessionBuilder.process(message);
PreKeyService.initiateRefresh(context, masterSecret); PreKeyService.initiateRefresh(context, masterSecret);
} }
public void processKeyExchangeMessage(PreKeyEntity message, long threadId) public void processKeyExchangeMessage(PreKeyEntity message, long threadId)
throws InvalidKeyException throws InvalidKeyException, UntrustedIdentityException
{ {
sessionBuilder.process(message); sessionBuilder.process(message);
@ -104,8 +72,8 @@ public class KeyExchangeProcessor {
} }
} }
public void processKeyExchangeMessage(KeyExchangeMessage message, long threadId) public OutgoingKeyExchangeMessage processKeyExchangeMessage(KeyExchangeMessage message, long threadId)
throws InvalidKeyException throws InvalidKeyException, UntrustedIdentityException, StaleKeyExchangeException
{ {
KeyExchangeMessage responseMessage = sessionBuilder.process(message); KeyExchangeMessage responseMessage = sessionBuilder.process(message);
Recipient recipient = RecipientFactory.getRecipientsForIds(context, Recipient recipient = RecipientFactory.getRecipientsForIds(context,
@ -113,15 +81,16 @@ public class KeyExchangeProcessor {
false) false)
.getPrimaryRecipient(); .getPrimaryRecipient();
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); DecryptingQueue.scheduleRogueMessages(context, masterSecret, recipient);
broadcastSecurityUpdateEvent(context, threadId); broadcastSecurityUpdateEvent(context, threadId);
if (responseMessage != null) {
String serializedResponse = Base64.encodeBytesWithoutPadding(responseMessage.serialize());
return new OutgoingKeyExchangeMessage(recipient, serializedResponse);
} else {
return null;
}
} }
public static void broadcastSecurityUpdateEvent(Context context, long threadId) { public static void broadcastSecurityUpdateEvent(Context context, long threadId) {

View file

@ -33,4 +33,10 @@ public class TextSecureIdentityKeyStore implements IdentityKeyStore {
public void saveIdentity(long recipientId, IdentityKey identityKey) { public void saveIdentity(long recipientId, IdentityKey identityKey) {
DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey); DatabaseFactory.getIdentityDatabase(context).saveIdentity(masterSecret, recipientId, identityKey);
} }
@Override
public boolean isTrustedIdentity(long recipientId, IdentityKey identityKey) {
return DatabaseFactory.getIdentityDatabase(context)
.isValidIdentity(masterSecret, recipientId, identityKey);
}
} }

View file

@ -27,6 +27,7 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.state.SessionStore; import org.whispersystems.libaxolotl.state.SessionStore;
import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.MasterSecret;
@ -117,12 +118,13 @@ public class PushReceiver {
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(message.getBody()); PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(message.getBody());
if (processor.isTrusted(preKeyExchange)) { try {
processor.processKeyExchangeMessage(preKeyExchange); processor.processKeyExchangeMessage(preKeyExchange);
IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getWhisperMessage().serialize()); IncomingPushMessage bundledMessage = message.withBody(preKeyExchange.getWhisperMessage().serialize());
handleReceivedSecureMessage(masterSecret, bundledMessage); handleReceivedSecureMessage(masterSecret, bundledMessage);
} else { } catch (UntrustedIdentityException uie) {
Log.w("PushReceiver", uie);
String encoded = Base64.encodeBytes(message.getBody()); String encoded = Base64.encodeBytes(message.getBody());
IncomingTextMessage textMessage = new IncomingTextMessage(message, encoded, null); IncomingTextMessage textMessage = new IncomingTextMessage(message, encoded, null);
IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded); IncomingPreKeyBundleMessage bundleMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);

View file

@ -32,17 +32,22 @@ import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory; import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException; import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage; import org.thoughtcrime.securesms.sms.IncomingEncryptedMessage;
import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage; import org.thoughtcrime.securesms.sms.IncomingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage; import org.thoughtcrime.securesms.sms.IncomingPreKeyBundleMessage;
import org.thoughtcrime.securesms.sms.IncomingTextMessage; import org.thoughtcrime.securesms.sms.IncomingTextMessage;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler; import org.thoughtcrime.securesms.sms.MultipartSmsMessageHandler;
import org.thoughtcrime.securesms.sms.OutgoingKeyExchangeMessage;
import org.thoughtcrime.securesms.sms.SmsTransportDetails; import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.thoughtcrime.securesms.util.TextSecurePreferences; import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.whispersystems.libaxolotl.InvalidKeyException; import org.whispersystems.libaxolotl.InvalidKeyException;
import org.whispersystems.libaxolotl.InvalidMessageException; import org.whispersystems.libaxolotl.InvalidMessageException;
import org.whispersystems.libaxolotl.InvalidVersionException; import org.whispersystems.libaxolotl.InvalidVersionException;
import org.whispersystems.libaxolotl.LegacyMessageException; import org.whispersystems.libaxolotl.LegacyMessageException;
import org.whispersystems.libaxolotl.StaleKeyExchangeException;
import org.whispersystems.libaxolotl.UntrustedIdentityException;
import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage; import org.whispersystems.libaxolotl.protocol.KeyExchangeMessage;
import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage; import org.whispersystems.libaxolotl.protocol.PreKeyWhisperMessage;
import org.whispersystems.libaxolotl.protocol.WhisperMessage; import org.whispersystems.libaxolotl.protocol.WhisperMessage;
@ -119,7 +124,6 @@ public class SmsReceiver {
byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes()); byte[] rawMessage = transportDetails.getDecodedMessage(message.getMessageBody().getBytes());
PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(rawMessage); PreKeyWhisperMessage preKeyExchange = new PreKeyWhisperMessage(rawMessage);
if (processor.isTrusted(preKeyExchange)) {
processor.processKeyExchangeMessage(preKeyExchange); processor.processKeyExchangeMessage(preKeyExchange);
WhisperMessage ciphertextMessage = preKeyExchange.getWhisperMessage(); WhisperMessage ciphertextMessage = preKeyExchange.getWhisperMessage();
@ -133,8 +137,7 @@ public class SmsReceiver {
context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION); context.sendBroadcast(intent, KeyCachingService.KEY_PERMISSION);
return messageAndThreadId; return messageAndThreadId;
} } catch (InvalidKeyException | RecipientFormattingException | InvalidMessageException | IOException e) {
} catch (InvalidKeyException e) {
Log.w("SmsReceiver", e); Log.w("SmsReceiver", e);
message.setCorrupted(true); message.setCorrupted(true);
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
@ -143,15 +146,8 @@ public class SmsReceiver {
} catch (InvalidKeyIdException e) { } catch (InvalidKeyIdException e) {
Log.w("SmsReceiver", e); Log.w("SmsReceiver", e);
message.setStale(true); message.setStale(true);
} catch (IOException e) { } catch (UntrustedIdentityException e) {
Log.w("SmsReceive", e);
message.setCorrupted(true);
} catch (InvalidMessageException e) {
Log.w("SmsReceiver", e); Log.w("SmsReceiver", e);
message.setCorrupted(true);
} catch (RecipientFormattingException e) {
Log.w("SmsReceiver", e);
message.setCorrupted(true);
} }
return storeStandardMessage(masterSecret, message); return storeStandardMessage(masterSecret, message);
@ -166,17 +162,18 @@ public class SmsReceiver {
RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId()); RecipientDevice recipientDevice = new RecipientDevice(recipient.getRecipientId(), message.getSenderDeviceId());
KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(Base64.decodeWithoutPadding(message.getMessageBody())); KeyExchangeMessage exchangeMessage = new KeyExchangeMessage(Base64.decodeWithoutPadding(message.getMessageBody()));
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipientDevice);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(new Recipients(recipient));
OutgoingKeyExchangeMessage response = processor.processKeyExchangeMessage(exchangeMessage, threadId);
if (processor.isStale(exchangeMessage)) {
message.setStale(true);
} else if (processor.isTrusted(exchangeMessage)) {
message.setProcessed(true); message.setProcessed(true);
Pair<Long, Long> messageAndThreadId = storeStandardMessage(masterSecret, message); Pair<Long, Long> messageAndThreadId = storeStandardMessage(masterSecret, message);
processor.processKeyExchangeMessage(exchangeMessage, messageAndThreadId.second);
if (response != null) {
MessageSender.send(context, masterSecret, response, messageAndThreadId.second, true);
}
return messageAndThreadId; return messageAndThreadId;
}
} catch (InvalidVersionException e) { } catch (InvalidVersionException e) {
Log.w("SmsReceiver", e); Log.w("SmsReceiver", e);
message.setInvalidVersion(true); message.setInvalidVersion(true);
@ -186,6 +183,11 @@ public class SmsReceiver {
} catch (LegacyMessageException e) { } catch (LegacyMessageException e) {
Log.w("SmsReceiver", e); Log.w("SmsReceiver", e);
message.setLegacyVersion(true); message.setLegacyVersion(true);
} catch (StaleKeyExchangeException e) {
Log.w("SmsReceiver", e);
message.setStale(true);
} catch (UntrustedIdentityException e) {
Log.w("SmsReceiver", e);
} }
} }

View file

@ -213,11 +213,12 @@ public class PushTransport extends BaseTransport {
PreKeyEntity preKey = socket.getPreKey(address); PreKeyEntity preKey = socket.getPreKey(address);
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, address); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, address);
if (processor.isTrusted(preKey)) { try {
processor.processKeyExchangeMessage(preKey, threadId); processor.processKeyExchangeMessage(preKey, threadId);
} else { } catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
throw new UntrustedIdentityException("Untrusted identity key!", e164number, preKey.getIdentityKey()); throw new UntrustedIdentityException("Untrusted identity key!", e164number, preKey.getIdentityKey());
} }
} }
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
throw new IOException(e); throw new IOException(e);
@ -332,9 +333,9 @@ public class PushTransport extends BaseTransport {
PushAddress device = PushAddress.create(context, pushAddress.getRecipientId(), pushAddress.getNumber(), preKey.getDeviceId()); PushAddress device = PushAddress.create(context, pushAddress.getRecipientId(), pushAddress.getNumber(), preKey.getDeviceId());
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, device); KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, device);
if (processor.isTrusted(preKey)) { try {
processor.processKeyExchangeMessage(preKey, threadId); processor.processKeyExchangeMessage(preKey, threadId);
} else { } catch (org.whispersystems.libaxolotl.UntrustedIdentityException e) {
throw new UntrustedIdentityException("Untrusted identity key!", pushAddress.getNumber(), preKey.getIdentityKey()); throw new UntrustedIdentityException("Untrusted identity key!", pushAddress.getNumber(), preKey.getIdentityKey());
} }
} }