diff --git a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java index 3f1edb34ce..bf861339a1 100644 --- a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java +++ b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java @@ -18,29 +18,258 @@ package org.whispersystems.textsecure.crypto; import android.content.Context; +import android.util.Log; +import android.util.Pair; +import org.whispersystems.textsecure.crypto.ecc.Curve; +import org.whispersystems.textsecure.crypto.ecc.ECKeyPair; +import org.whispersystems.textsecure.crypto.ecc.ECPublicKey; import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage; +import org.whispersystems.textsecure.crypto.protocol.PreKeyWhisperMessage; +import org.whispersystems.textsecure.crypto.protocol.WhisperMessage; +import org.whispersystems.textsecure.crypto.ratchet.ChainKey; +import org.whispersystems.textsecure.crypto.ratchet.MessageKeys; +import org.whispersystems.textsecure.crypto.ratchet.RootKey; import org.whispersystems.textsecure.storage.RecipientDevice; -import org.whispersystems.textsecure.storage.SessionRecordV1; import org.whispersystems.textsecure.storage.SessionRecordV2; +import org.whispersystems.textsecure.storage.SessionState; +import org.whispersystems.textsecure.util.Conversions; -public abstract class SessionCipher { +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.List; - protected static final Object SESSION_LOCK = new Object(); +import javax.crypto.BadPaddingException; +import javax.crypto.Cipher; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; - public abstract CiphertextMessage encrypt(byte[] paddedMessage); - public abstract byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException, DuplicateMessageException, LegacyMessageException; - public abstract int getRemoteRegistrationId(); +public class SessionCipher { + + private static final Object SESSION_LOCK = new Object(); + + private final Context context; + private final MasterSecret masterSecret; + private final RecipientDevice recipient; public static SessionCipher createFor(Context context, MasterSecret masterSecret, RecipientDevice recipient) { if (SessionRecordV2.hasSession(context, masterSecret, recipient)) { - return new SessionCipherV2(context, masterSecret, recipient); + return new SessionCipher(context, masterSecret, recipient); } else { throw new AssertionError("Attempt to initialize cipher for non-existing session."); } } + + private SessionCipher(Context context, MasterSecret masterSecret, RecipientDevice recipient) { + this.recipient = recipient; + this.masterSecret = masterSecret; + this.context = context; + } + + public CiphertextMessage encrypt(byte[] paddedMessage) { + synchronized (SESSION_LOCK) { + SessionRecordV2 sessionRecord = getSessionRecord(); + SessionState sessionState = sessionRecord.getSessionState(); + ChainKey chainKey = sessionState.getSenderChainKey(); + MessageKeys messageKeys = chainKey.getMessageKeys(); + ECPublicKey senderEphemeral = sessionState.getSenderEphemeral(); + int previousCounter = sessionState.getPreviousCounter(); + + byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage); + CiphertextMessage ciphertextMessage = new WhisperMessage(messageKeys.getMacKey(), + senderEphemeral, chainKey.getIndex(), + previousCounter, ciphertextBody); + + if (sessionState.hasPendingPreKey()) { + Pair pendingPreKey = sessionState.getPendingPreKey(); + int localRegistrationId = sessionState.getLocalRegistrationId(); + + ciphertextMessage = new PreKeyWhisperMessage(localRegistrationId, pendingPreKey.first, + pendingPreKey.second, + sessionState.getLocalIdentityKey(), + (WhisperMessage) ciphertextMessage); + } + + sessionState.setSenderChainKey(chainKey.getNextChainKey()); + sessionRecord.save(); + + return ciphertextMessage; + } + } + + public byte[] decrypt(byte[] decodedMessage) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException + { + synchronized (SESSION_LOCK) { + SessionRecordV2 sessionRecord = getSessionRecord(); + SessionState sessionState = sessionRecord.getSessionState(); + List previousStates = sessionRecord.getPreviousSessions(); + + try { + byte[] plaintext = decrypt(sessionState, decodedMessage); + sessionRecord.save(); + + return plaintext; + } catch (InvalidMessageException e) { + Log.w("SessionCipherV2", e); + } + + for (SessionState previousState : previousStates) { + try { + Log.w("SessionCipherV2", "Attempting decrypt on previous state..."); + byte[] plaintext = decrypt(previousState, decodedMessage); + sessionRecord.save(); + + return plaintext; + } catch (InvalidMessageException e) { + Log.w("SessionCipherV2", e); + } + } + + throw new InvalidMessageException("No valid sessions."); + } + } + + public byte[] decrypt(SessionState sessionState, byte[] decodedMessage) + throws InvalidMessageException, DuplicateMessageException, LegacyMessageException + { + if (!sessionState.hasSenderChain()) { + throw new InvalidMessageException("Uninitialized session!"); + } + + WhisperMessage ciphertextMessage = new WhisperMessage(decodedMessage); + ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral(); + int counter = ciphertextMessage.getCounter(); + ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); + MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, + chainKey, counter); + + ciphertextMessage.verifyMac(messageKeys.getMacKey()); + + byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody()); + + sessionState.clearPendingPreKey(); + + return plaintext; + + } + + public int getRemoteRegistrationId() { + synchronized (SESSION_LOCK) { + SessionRecordV2 sessionRecord = getSessionRecord(); + return sessionRecord.getSessionState().getRemoteRegistrationId(); + } + } + + private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) + throws InvalidMessageException + { + try { + if (sessionState.hasReceiverChain(theirEphemeral)) { + return sessionState.getReceiverChainKey(theirEphemeral); + } else { + RootKey rootKey = sessionState.getRootKey(); + ECKeyPair ourEphemeral = sessionState.getSenderEphemeralPair(); + Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); + ECKeyPair ourNewEphemeral = Curve.generateKeyPair(true); + Pair senderChain = receiverChain.first.createChain(theirEphemeral, ourNewEphemeral); + + sessionState.setRootKey(senderChain.first); + sessionState.addReceiverChain(theirEphemeral, receiverChain.second); + sessionState.setPreviousCounter(sessionState.getSenderChainKey().getIndex()-1); + sessionState.setSenderChain(ourNewEphemeral, senderChain.second); + + return receiverChain.second; + } + } catch (InvalidKeyException e) { + throw new InvalidMessageException(e); + } + } + + private MessageKeys getOrCreateMessageKeys(SessionState sessionState, + ECPublicKey theirEphemeral, + ChainKey chainKey, int counter) + throws InvalidMessageException, DuplicateMessageException + { + if (chainKey.getIndex() > counter) { + if (sessionState.hasMessageKeys(theirEphemeral, counter)) { + return sessionState.removeMessageKeys(theirEphemeral, counter); + } else { + throw new DuplicateMessageException("Received message with old counter: " + + chainKey.getIndex() + " , " + counter); + } + } + + if (chainKey.getIndex() - counter > 2000) { + throw new InvalidMessageException("Over 2000 messages into the future!"); + } + + while (chainKey.getIndex() < counter) { + MessageKeys messageKeys = chainKey.getMessageKeys(); + sessionState.setMessageKeys(theirEphemeral, messageKeys); + chainKey = chainKey.getNextChainKey(); + } + + sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); + return chainKey.getMessageKeys(); + } + + private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) { + try { + Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, + messageKeys.getCipherKey(), + messageKeys.getCounter()); + + return cipher.doFinal(plaintext); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText) { + try { + Cipher cipher = getCipher(Cipher.DECRYPT_MODE, + messageKeys.getCipherKey(), + messageKeys.getCounter()); + return cipher.doFinal(cipherText); + } catch (IllegalBlockSizeException e) { + throw new AssertionError(e); + } catch (BadPaddingException e) { + throw new AssertionError(e); + } + } + + private Cipher getCipher(int mode, SecretKeySpec key, int counter) { + try { + Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); + + byte[] ivBytes = new byte[16]; + Conversions.intToByteArray(ivBytes, 0, counter); + + IvParameterSpec iv = new IvParameterSpec(ivBytes); + cipher.init(mode, key, iv); + + return cipher; + } catch (NoSuchAlgorithmException e) { + throw new AssertionError(e); + } catch (NoSuchPaddingException e) { + throw new AssertionError(e); + } catch (java.security.InvalidKeyException e) { + throw new AssertionError(e); + } catch (InvalidAlgorithmParameterException e) { + throw new AssertionError(e); + } + } + + private SessionRecordV2 getSessionRecord() { + return new SessionRecordV2(context, masterSecret, recipient); + } } \ No newline at end of file diff --git a/library/src/org/whispersystems/textsecure/crypto/SessionCipherV2.java b/library/src/org/whispersystems/textsecure/crypto/SessionCipherV2.java deleted file mode 100644 index f1f9846f42..0000000000 --- a/library/src/org/whispersystems/textsecure/crypto/SessionCipherV2.java +++ /dev/null @@ -1,252 +0,0 @@ -package org.whispersystems.textsecure.crypto; - -import android.content.Context; -import android.util.Log; -import android.util.Pair; - -import org.whispersystems.textsecure.crypto.ecc.Curve; -import org.whispersystems.textsecure.crypto.ecc.ECKeyPair; -import org.whispersystems.textsecure.crypto.ecc.ECPublicKey; -import org.whispersystems.textsecure.crypto.protocol.CiphertextMessage; -import org.whispersystems.textsecure.crypto.protocol.PreKeyWhisperMessage; -import org.whispersystems.textsecure.crypto.protocol.WhisperMessageV2; -import org.whispersystems.textsecure.crypto.ratchet.ChainKey; -import org.whispersystems.textsecure.crypto.ratchet.MessageKeys; -import org.whispersystems.textsecure.crypto.ratchet.RootKey; -import org.whispersystems.textsecure.storage.RecipientDevice; -import org.whispersystems.textsecure.storage.SessionRecordV2; -import org.whispersystems.textsecure.storage.SessionState; -import org.whispersystems.textsecure.util.Conversions; - -import java.security.InvalidAlgorithmParameterException; -import java.security.NoSuchAlgorithmException; -import java.util.List; - -import javax.crypto.BadPaddingException; -import javax.crypto.Cipher; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; - -public class SessionCipherV2 extends SessionCipher { - - private final Context context; - private final MasterSecret masterSecret; - private final RecipientDevice recipient; - - public SessionCipherV2(Context context, - MasterSecret masterSecret, - RecipientDevice recipient) - { - this.context = context; - this.masterSecret = masterSecret; - this.recipient = recipient; - } - - @Override - public CiphertextMessage encrypt(byte[] paddedMessage) { - synchronized (SESSION_LOCK) { - SessionRecordV2 sessionRecord = getSessionRecord(); - SessionState sessionState = sessionRecord.getSessionState(); - ChainKey chainKey = sessionState.getSenderChainKey(); - MessageKeys messageKeys = chainKey.getMessageKeys(); - ECPublicKey senderEphemeral = sessionState.getSenderEphemeral(); - int previousCounter = sessionState.getPreviousCounter(); - - byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage); - CiphertextMessage ciphertextMessage = new WhisperMessageV2(messageKeys.getMacKey(), - senderEphemeral, chainKey.getIndex(), - previousCounter, ciphertextBody); - - if (sessionState.hasPendingPreKey()) { - Pair pendingPreKey = sessionState.getPendingPreKey(); - int localRegistrationId = sessionState.getLocalRegistrationId(); - - ciphertextMessage = new PreKeyWhisperMessage(localRegistrationId, pendingPreKey.first, - pendingPreKey.second, - sessionState.getLocalIdentityKey(), - (WhisperMessageV2) ciphertextMessage); - } - - sessionState.setSenderChainKey(chainKey.getNextChainKey()); - sessionRecord.save(); - - return ciphertextMessage; - } - } - - @Override - public byte[] decrypt(byte[] decodedMessage) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException - { - synchronized (SESSION_LOCK) { - SessionRecordV2 sessionRecord = getSessionRecord(); - SessionState sessionState = sessionRecord.getSessionState(); - List previousStates = sessionRecord.getPreviousSessions(); - - try { - byte[] plaintext = decrypt(sessionState, decodedMessage); - sessionRecord.save(); - - return plaintext; - } catch (InvalidMessageException e) { - Log.w("SessionCipherV2", e); - } - - for (SessionState previousState : previousStates) { - try { - Log.w("SessionCipherV2", "Attempting decrypt on previous state..."); - byte[] plaintext = decrypt(previousState, decodedMessage); - sessionRecord.save(); - - return plaintext; - } catch (InvalidMessageException e) { - Log.w("SessionCipherV2", e); - } - } - - throw new InvalidMessageException("No valid sessions."); - } - } - - public byte[] decrypt(SessionState sessionState, byte[] decodedMessage) - throws InvalidMessageException, DuplicateMessageException, LegacyMessageException - { - if (!sessionState.hasSenderChain()) { - throw new InvalidMessageException("Uninitialized session!"); - } - - WhisperMessageV2 ciphertextMessage = new WhisperMessageV2(decodedMessage); - ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral(); - int counter = ciphertextMessage.getCounter(); - ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral); - MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral, - chainKey, counter); - - ciphertextMessage.verifyMac(messageKeys.getMacKey()); - - byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody()); - - sessionState.clearPendingPreKey(); - - return plaintext; - - } - - @Override - public int getRemoteRegistrationId() { - synchronized (SESSION_LOCK) { - SessionRecordV2 sessionRecord = getSessionRecord(); - return sessionRecord.getSessionState().getRemoteRegistrationId(); - } - } - - private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral) - throws InvalidMessageException - { - try { - if (sessionState.hasReceiverChain(theirEphemeral)) { - return sessionState.getReceiverChainKey(theirEphemeral); - } else { - RootKey rootKey = sessionState.getRootKey(); - ECKeyPair ourEphemeral = sessionState.getSenderEphemeralPair(); - Pair receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral); - ECKeyPair ourNewEphemeral = Curve.generateKeyPair(true); - Pair senderChain = receiverChain.first.createChain(theirEphemeral, ourNewEphemeral); - - sessionState.setRootKey(senderChain.first); - sessionState.addReceiverChain(theirEphemeral, receiverChain.second); - sessionState.setPreviousCounter(sessionState.getSenderChainKey().getIndex()-1); - sessionState.setSenderChain(ourNewEphemeral, senderChain.second); - - return receiverChain.second; - } - } catch (InvalidKeyException e) { - throw new InvalidMessageException(e); - } - } - - private MessageKeys getOrCreateMessageKeys(SessionState sessionState, - ECPublicKey theirEphemeral, - ChainKey chainKey, int counter) - throws InvalidMessageException, DuplicateMessageException - { - if (chainKey.getIndex() > counter) { - if (sessionState.hasMessageKeys(theirEphemeral, counter)) { - return sessionState.removeMessageKeys(theirEphemeral, counter); - } else { - throw new DuplicateMessageException("Received message with old counter: " + - chainKey.getIndex() + " , " + counter); - } - } - - if (chainKey.getIndex() - counter > 2000) { - throw new InvalidMessageException("Over 2000 messages into the future!"); - } - - while (chainKey.getIndex() < counter) { - MessageKeys messageKeys = chainKey.getMessageKeys(); - sessionState.setMessageKeys(theirEphemeral, messageKeys); - chainKey = chainKey.getNextChainKey(); - } - - sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey()); - return chainKey.getMessageKeys(); - } - - private byte[] getCiphertext(MessageKeys messageKeys, byte[] plaintext) { - try { - Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, - messageKeys.getCipherKey(), - messageKeys.getCounter()); - - return cipher.doFinal(plaintext); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private byte[] getPlaintext(MessageKeys messageKeys, byte[] cipherText) { - try { - Cipher cipher = getCipher(Cipher.DECRYPT_MODE, - messageKeys.getCipherKey(), - messageKeys.getCounter()); - return cipher.doFinal(cipherText); - } catch (IllegalBlockSizeException e) { - throw new AssertionError(e); - } catch (BadPaddingException e) { - throw new AssertionError(e); - } - } - - private Cipher getCipher(int mode, SecretKeySpec key, int counter) { - try { - Cipher cipher = Cipher.getInstance("AES/CTR/NoPadding"); - - byte[] ivBytes = new byte[16]; - Conversions.intToByteArray(ivBytes, 0, counter); - - IvParameterSpec iv = new IvParameterSpec(ivBytes); - cipher.init(mode, key, iv); - - return cipher; - } catch (NoSuchAlgorithmException e) { - throw new AssertionError(e); - } catch (NoSuchPaddingException e) { - throw new AssertionError(e); - } catch (java.security.InvalidKeyException e) { - throw new AssertionError(e); - } catch (InvalidAlgorithmParameterException e) { - throw new AssertionError(e); - } - } - - - private SessionRecordV2 getSessionRecord() { - return new SessionRecordV2(context, masterSecret, recipient); - } - -} diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyWhisperMessage.java b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyWhisperMessage.java index 2aaa915010..5845dc0447 100644 --- a/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyWhisperMessage.java +++ b/library/src/org/whispersystems/textsecure/crypto/protocol/PreKeyWhisperMessage.java @@ -15,13 +15,13 @@ import org.whispersystems.textsecure.util.Util; public class PreKeyWhisperMessage implements CiphertextMessage { - private final int version; - private final int registrationId; - private final int preKeyId; - private final ECPublicKey baseKey; - private final IdentityKey identityKey; - private final WhisperMessageV2 message; - private final byte[] serialized; + private final int version; + private final int registrationId; + private final int preKeyId; + private final ECPublicKey baseKey; + private final IdentityKey identityKey; + private final WhisperMessage message; + private final byte[] serialized; public PreKeyWhisperMessage(byte[] serialized) throws InvalidMessageException, InvalidVersionException @@ -50,7 +50,7 @@ public class PreKeyWhisperMessage implements CiphertextMessage { this.preKeyId = preKeyWhisperMessage.getPreKeyId(); this.baseKey = Curve.decodePoint(preKeyWhisperMessage.getBaseKey().toByteArray(), 0); this.identityKey = new IdentityKey(Curve.decodePoint(preKeyWhisperMessage.getIdentityKey().toByteArray(), 0)); - this.message = new WhisperMessageV2(preKeyWhisperMessage.getMessage().toByteArray()); + this.message = new WhisperMessage(preKeyWhisperMessage.getMessage().toByteArray()); } catch (InvalidProtocolBufferException e) { throw new InvalidMessageException(e); } catch (InvalidKeyException e) { @@ -61,7 +61,7 @@ public class PreKeyWhisperMessage implements CiphertextMessage { } public PreKeyWhisperMessage(int registrationId, int preKeyId, ECPublicKey baseKey, - IdentityKey identityKey, WhisperMessageV2 message) + IdentityKey identityKey, WhisperMessage message) { this.version = CiphertextMessage.CURRENT_VERSION; this.registrationId = registrationId; @@ -98,7 +98,7 @@ public class PreKeyWhisperMessage implements CiphertextMessage { return baseKey; } - public WhisperMessageV2 getWhisperMessage() { + public WhisperMessage getWhisperMessage() { return message; } diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessageV2.java b/library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessage.java similarity index 86% rename from library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessageV2.java rename to library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessage.java index 32df62adbc..7b20211cd7 100644 --- a/library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessageV2.java +++ b/library/src/org/whispersystems/textsecure/crypto/protocol/WhisperMessage.java @@ -1,7 +1,5 @@ package org.whispersystems.textsecure.crypto.protocol; -import android.util.Log; - import com.google.protobuf.ByteString; import com.google.protobuf.InvalidProtocolBufferException; @@ -10,19 +8,17 @@ import org.whispersystems.textsecure.crypto.InvalidMessageException; import org.whispersystems.textsecure.crypto.LegacyMessageException; import org.whispersystems.textsecure.crypto.ecc.Curve; import org.whispersystems.textsecure.crypto.ecc.ECPublicKey; -import org.whispersystems.textsecure.crypto.protocol.WhisperProtos.WhisperMessage; import org.whispersystems.textsecure.util.Conversions; -import org.whispersystems.textsecure.util.Hex; import org.whispersystems.textsecure.util.Util; +import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.text.ParseException; -import java.util.Arrays; import javax.crypto.Mac; import javax.crypto.spec.SecretKeySpec; -public class WhisperMessageV2 implements CiphertextMessage { +public class WhisperMessage implements CiphertextMessage { private static final int MAC_LENGTH = 8; @@ -32,7 +28,7 @@ public class WhisperMessageV2 implements CiphertextMessage { private final byte[] ciphertext; private final byte[] serialized; - public WhisperMessageV2(byte[] serialized) throws InvalidMessageException, LegacyMessageException { + public WhisperMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException { try { byte[][] messageParts = Util.split(serialized, 1, serialized.length - 1 - MAC_LENGTH, MAC_LENGTH); byte version = messageParts[0][0]; @@ -47,7 +43,7 @@ public class WhisperMessageV2 implements CiphertextMessage { throw new InvalidMessageException("Unknown version: " + Conversions.highBitsToInt(version)); } - WhisperMessage whisperMessage = WhisperMessage.parseFrom(message); + WhisperProtos.WhisperMessage whisperMessage = WhisperProtos.WhisperMessage.parseFrom(message); if (!whisperMessage.hasCiphertext() || !whisperMessage.hasCounter() || @@ -70,11 +66,11 @@ public class WhisperMessageV2 implements CiphertextMessage { } } - public WhisperMessageV2(SecretKeySpec macKey, ECPublicKey senderEphemeral, - int counter, int previousCounter, byte[] ciphertext) + public WhisperMessage(SecretKeySpec macKey, ECPublicKey senderEphemeral, + int counter, int previousCounter, byte[] ciphertext) { byte[] version = {Conversions.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; - byte[] message = WhisperMessage.newBuilder() + byte[] message = WhisperProtos.WhisperMessage.newBuilder() .setEphemeralKey(ByteString.copyFrom(senderEphemeral.serialize())) .setCounter(counter) .setPreviousCounter(previousCounter) @@ -108,7 +104,7 @@ public class WhisperMessageV2 implements CiphertextMessage { byte[] ourMac = getMac(macKey, parts[0]); byte[] theirMac = parts[1]; - if (!Arrays.equals(ourMac, theirMac)) { + if (!MessageDigest.isEqual(ourMac, theirMac)) { throw new InvalidMessageException("Bad Mac!"); } } diff --git a/src/org/thoughtcrime/securesms/service/SmsReceiver.java b/src/org/thoughtcrime/securesms/service/SmsReceiver.java index 786d596865..4d16769981 100644 --- a/src/org/thoughtcrime/securesms/service/SmsReceiver.java +++ b/src/org/thoughtcrime/securesms/service/SmsReceiver.java @@ -47,7 +47,7 @@ import org.whispersystems.textsecure.crypto.InvalidMessageException; import org.whispersystems.textsecure.crypto.InvalidVersionException; import org.whispersystems.textsecure.crypto.MasterSecret; import org.whispersystems.textsecure.crypto.protocol.PreKeyWhisperMessage; -import org.whispersystems.textsecure.crypto.protocol.WhisperMessageV2; +import org.whispersystems.textsecure.crypto.protocol.WhisperMessage; import org.whispersystems.textsecure.storage.InvalidKeyIdException; import org.whispersystems.textsecure.storage.RecipientDevice; @@ -122,7 +122,7 @@ public class SmsReceiver { if (processor.isTrusted(preKeyExchange)) { processor.processKeyExchangeMessage(preKeyExchange); - WhisperMessageV2 ciphertextMessage = preKeyExchange.getWhisperMessage(); + WhisperMessage ciphertextMessage = preKeyExchange.getWhisperMessage(); String bundledMessageBody = new String(transportDetails.getEncodedMessage(ciphertextMessage.serialize())); IncomingEncryptedMessage bundledMessage = new IncomingEncryptedMessage(message, bundledMessageBody); Pair messageAndThreadId = storeSecureMessage(masterSecret, bundledMessage);