diff --git a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java
index 0a6cb5cc10..c93cd8831b 100644
--- a/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java
+++ b/library/src/org/whispersystems/textsecure/crypto/SessionCipher.java
@@ -22,7 +22,7 @@ import android.util.Log;
import org.spongycastle.crypto.AsymmetricCipherKeyPair;
import org.spongycastle.crypto.agreement.ECDHBasicAgreement;
import org.spongycastle.crypto.params.ECPublicKeyParameters;
-import org.whispersystems.textsecure.crypto.protocol.Message;
+import org.whispersystems.textsecure.crypto.protocol.EncryptedMessage;
import org.whispersystems.textsecure.storage.CanonicalRecipientAddress;
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
@@ -37,7 +37,6 @@ import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
-import java.io.IOException;
import java.math.BigInteger;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
@@ -57,85 +56,97 @@ public class SessionCipher {
public static final int CIPHER_KEY_LENGTH = 16;
public static final int MAC_KEY_LENGTH = 20;
- public static final int ENCRYPTED_MESSAGE_OVERHEAD = Message.HEADER_LENGTH + MessageMac.MAC_LENGTH;
- // public static final int ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE = SmsTransportDetails.SINGLE_MESSAGE_MAX_BYTES - ENCRYPTED_MESSAGE_OVERHEAD;
-
- private final LocalKeyRecord localRecord;
- private final RemoteKeyRecord remoteRecord;
- private final SessionRecord sessionRecord;
- private final MasterSecret masterSecret;
- private final TransportDetails transportDetails;
-
- public SessionCipher(Context context, MasterSecret masterSecret, CanonicalRecipientAddress recipient, TransportDetails transportDetails) {
- Log.w("SessionCipher", "Constructing session cipher...");
- this.masterSecret = masterSecret;
- this.localRecord = new LocalKeyRecord(context, masterSecret, recipient);
- this.remoteRecord = new RemoteKeyRecord(context, recipient);
- this.sessionRecord = new SessionRecord(context, masterSecret, recipient);
- this.transportDetails = transportDetails;
- }
-
- public byte[] encryptMessage(byte[] messageText) {
- Log.w("SessionCipher", "Encrypting message...");
- try {
- int localId = localRecord.getCurrentKeyPair().getId();
- int remoteId = remoteRecord.getCurrentRemoteKey().getId();
- SessionKey sessionKey = getSessionKey(Cipher.ENCRYPT_MODE, localId, remoteId);
- byte[]paddedMessage = transportDetails.getPaddedMessageBody(messageText);
- byte[]cipherText = getCiphertext(paddedMessage, sessionKey.getCipherKey());
- byte[]message = buildMessageFromCiphertext(cipherText);
- byte[]messageWithMac = MessageMac.buildMessageWithMac(message, sessionKey.getMacKey());
+ public static final int ENCRYPTED_MESSAGE_OVERHEAD = EncryptedMessage.HEADER_LENGTH + MessageMac.MAC_LENGTH;
- sessionRecord.setSessionKey(sessionKey);
- sessionRecord.incrementCounter();
- sessionRecord.save();
-
- return transportDetails.encodeMessage(messageWithMac);
- } catch (IllegalBlockSizeException e) {
- throw new IllegalArgumentException(e);
- } catch (BadPaddingException e) {
- throw new IllegalArgumentException(e);
+ public SessionCipherContext getEncryptionContext(Context context, MasterSecret masterSecret,
+ CanonicalRecipientAddress recipient)
+ {
+ try {
+ KeyRecords records = getKeyRecords(context, masterSecret, recipient);
+ int localKeyId = records.getLocalKeyRecord().getCurrentKeyPair().getId();
+ int remoteKeyId = records.getRemoteKeyRecord().getCurrentRemoteKey().getId();
+ SessionKey sessionKey = getSessionKey(masterSecret, Cipher.ENCRYPT_MODE, records, localKeyId, remoteKeyId);
+ PublicKey nextKey = records.getLocalKeyRecord().getNextKeyPair().getPublicKey();
+ int counter = records.getSessionRecord().getCounter();
+ int negotiatedVersion = records.getSessionRecord().getSessionVersion();
+
+ return new SessionCipherContext(records, sessionKey, localKeyId, remoteKeyId, nextKey, counter, negotiatedVersion);
} catch (InvalidKeyIdException e) {
throw new IllegalArgumentException(e);
}
}
-
+
+ public SessionCipherContext getDecryptionContext(Context context, MasterSecret masterSecret,
+ CanonicalRecipientAddress recipient,
+ int senderKeyId, int recipientKeyId,
+ PublicKey nextKey, int counter,
+ int negotiatedVersion)
+ throws InvalidMessageException
+ {
+ try {
+ KeyRecords records = getKeyRecords(context, masterSecret, recipient);
+ SessionKey sessionKey = getSessionKey(masterSecret, Cipher.DECRYPT_MODE, records, recipientKeyId, senderKeyId);
+ return new SessionCipherContext(records, sessionKey, senderKeyId, recipientKeyId, nextKey, counter, negotiatedVersion);
+ } catch (InvalidKeyIdException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+ public byte[] encrypt(SessionCipherContext context, byte[] paddedMessageBody) {
+ Log.w("SessionCipher", "Encrypting message...");
+ try {
+ byte[]cipherText = getCiphertext(paddedMessageBody, context.getSessionKey().getCipherKey(), context.getSessionRecord().getCounter());
+
+ context.getSessionRecord().setSessionKey(context.getSessionKey());
+ context.getSessionRecord().incrementCounter();
+ context.getSessionRecord().save();
+
+ return cipherText;
+ } catch (IllegalBlockSizeException e) {
+ throw new IllegalArgumentException(e);
+ } catch (BadPaddingException e) {
+ throw new IllegalArgumentException(e);
+ }
+ }
- public byte[] decryptMessage(byte[] messageText) throws InvalidMessageException {
+ public byte[] decrypt(SessionCipherContext context, byte[] decodedCiphertext)
+ throws InvalidMessageException
+ {
Log.w("SessionCipher", "Decrypting message...");
try {
- byte[] decodedMessage = transportDetails.decodeMessage(messageText);
- Message message = new Message(MessageMac.getMessageWithoutMac(decodedMessage));
- SessionKey sessionKey = getSessionKey(Cipher.DECRYPT_MODE, message.getReceiverKeyId(), message.getSenderKeyId());
+ byte[] plaintextWithPadding = getPlaintext(decodedCiphertext, context.getSessionKey().getCipherKey(), context.getCounter());
+
+ context.getRemoteKeyRecord().updateCurrentRemoteKey(context.getNextKey());
+ context.getRemoteKeyRecord().save();
- MessageMac.verifyMac(decodedMessage, sessionKey.getMacKey());
+ context.getLocalKeyRecord().advanceKeyIfNecessary(context.getRecipientKeyId());
+ context.getLocalKeyRecord().save();
- byte[] plaintextWithPadding = getPlaintext(message.getMessageText(), sessionKey.getCipherKey(), message.getCounter());
- byte[] plaintext = transportDetails.stripPaddedMessage(plaintextWithPadding);
+ context.getSessionRecord().setSessionKey(context.getSessionKey());
+ context.getSessionRecord().setSessionVersion(context.getNegotiatedVersion());
+ context.getSessionRecord().save();
- remoteRecord.updateCurrentRemoteKey(message.getNextKey());
- remoteRecord.save();
-
- localRecord.advanceKeyIfNecessary(message.getReceiverKeyId());
- localRecord.save();
-
- sessionRecord.setSessionKey(sessionKey);
- sessionRecord.setSessionVersion(message.getHighestMutuallySupportedVersion());
- sessionRecord.save();
-
- return plaintext;
- } catch (IOException e) {
- throw new InvalidMessageException("Encoding Failure", e);
- } catch (InvalidKeyIdException e) {
- throw new InvalidMessageException("Bad Key ID", e);
- } catch (InvalidMacException e) {
- throw new InvalidMessageException("Bad MAC", e);
+ return plaintextWithPadding;
} catch (IllegalBlockSizeException e) {
throw new InvalidMessageException("assert", e);
} catch (BadPaddingException e) {
throw new InvalidMessageException("assert", e);
}
}
+
+ public byte[] mac(SessionCipherContext context, byte[] formattedCiphertext) {
+ return MessageMac.buildMessageWithMac(formattedCiphertext, context.getSessionKey().getMacKey());
+ }
+
+ public void verifyMac(SessionCipherContext context, byte[] decodedCiphertext)
+ throws InvalidMessageException
+ {
+ try {
+ MessageMac.verifyMac(decodedCiphertext, context.getSessionKey().getMacKey());
+ } catch (InvalidMacException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
private SecretKeySpec deriveMacSecret(SecretKeySpec key) {
try {
@@ -147,25 +158,14 @@ public class SessionCipher {
throw new IllegalArgumentException("SHA-1 Not Supported!",e);
}
}
-
- private byte[] buildMessageFromCiphertext(byte[] cipherText) {
- Message message = new Message(localRecord.getCurrentKeyPair().getId(),
- remoteRecord.getCurrentRemoteKey().getId(),
- localRecord.getNextKeyPair().getPublicKey(),
- sessionRecord.getCounter(),
- cipherText, sessionRecord.getSessionVersion(), Message.SUPPORTED_VERSION);
-
- return message.serialize();
- }
-
private byte[] getPlaintext(byte[] cipherText, SecretKeySpec key, int counter) throws IllegalBlockSizeException, BadPaddingException {
Cipher cipher = getCipher(Cipher.DECRYPT_MODE, key, counter);
return cipher.doFinal(cipherText);
}
- private byte[] getCiphertext(byte[] message, SecretKeySpec key) throws IllegalBlockSizeException, BadPaddingException {
- Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, key, sessionRecord.getCounter());
+ private byte[] getCiphertext(byte[] message, SecretKeySpec key, int counter) throws IllegalBlockSizeException, BadPaddingException {
+ Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, key, counter);
return cipher.doFinal(message);
}
@@ -193,12 +193,16 @@ public class SessionCipher {
}
}
- private SecretKeySpec deriveCipherSecret(int mode, BigInteger sharedSecret, int localKeyId, int remoteKeyId) throws InvalidKeyIdException {
+ private SecretKeySpec deriveCipherSecret(int mode, BigInteger sharedSecret,
+ KeyRecords records, int localKeyId,
+ int remoteKeyId)
+ throws InvalidKeyIdException
+ {
byte[] sharedSecretBytes = sharedSecret.toByteArray();
byte[] derivedBytes = deriveBytes(sharedSecretBytes, 16 * 2);
byte[] cipherSecret = new byte[16];
- boolean isLowEnd = isLowEnd(localKeyId, remoteKeyId);
+ boolean isLowEnd = isLowEnd(records, localKeyId, remoteKeyId);
isLowEnd = (mode == Cipher.ENCRYPT_MODE ? isLowEnd : !isLowEnd);
if (isLowEnd) {
@@ -210,9 +214,11 @@ public class SessionCipher {
return new SecretKeySpec(cipherSecret, "AES");
}
- private boolean isLowEnd(int localKeyId, int remoteKeyId) throws InvalidKeyIdException {
- ECPublicKeyParameters localPublic = (ECPublicKeyParameters)localRecord.getKeyPairForId(localKeyId).getPublicKey().getKey();
- ECPublicKeyParameters remotePublic = (ECPublicKeyParameters)remoteRecord.getKeyForId(remoteKeyId).getKey();
+ private boolean isLowEnd(KeyRecords records, int localKeyId, int remoteKeyId)
+ throws InvalidKeyIdException
+ {
+ ECPublicKeyParameters localPublic = records.getLocalKeyRecord().getKeyPairForId(localKeyId).getPublicKey().getKey();
+ ECPublicKeyParameters remotePublic = records.getRemoteKeyRecord().getKeyForId(remoteKeyId).getKey();
BigInteger local = localPublic.getQ().getX().toBigInteger();
BigInteger remote = remotePublic.getQ().getX().toBigInteger();
@@ -240,26 +246,133 @@ public class SessionCipher {
return md.digest();
}
- private SessionKey getSessionKey(int mode, int localKeyId, int remoteKeyId) throws InvalidKeyIdException {
+ private SessionKey getSessionKey(MasterSecret masterSecret, int mode, KeyRecords records,
+ int localKeyId, int remoteKeyId)
+ throws InvalidKeyIdException
+ {
Log.w("SessionCipher", "Getting session key for local: " + localKeyId + " remote: " + remoteKeyId);
- SessionKey sessionKey = sessionRecord.getSessionKey(localKeyId, remoteKeyId);
- if (sessionKey != null) return sessionKey;
+ SessionKey sessionKey = records.getSessionRecord().getSessionKey(localKeyId, remoteKeyId);
+
+ if (sessionKey != null)
+ return sessionKey;
- BigInteger sharedSecret = calculateSharedSecret(localKeyId, remoteKeyId);
- SecretKeySpec cipherKey = deriveCipherSecret(mode, sharedSecret, localKeyId, remoteKeyId);
+ BigInteger sharedSecret = calculateSharedSecret(records, localKeyId, remoteKeyId);
+ SecretKeySpec cipherKey = deriveCipherSecret(mode, sharedSecret, records, localKeyId, remoteKeyId);
SecretKeySpec macKey = deriveMacSecret(cipherKey);
return new SessionKey(localKeyId, remoteKeyId, cipherKey, macKey, masterSecret);
}
- private BigInteger calculateSharedSecret(int localKeyId, int remoteKeyId) throws InvalidKeyIdException {
+ private BigInteger calculateSharedSecret(KeyRecords records, int localKeyId, int remoteKeyId)
+ throws InvalidKeyIdException
+ {
ECDHBasicAgreement agreement = new ECDHBasicAgreement();
- AsymmetricCipherKeyPair localKeyPair = localRecord.getKeyPairForId(localKeyId).getKeyPair();
- ECPublicKeyParameters remoteKey = remoteRecord.getKeyForId(remoteKeyId).getKey();
+ AsymmetricCipherKeyPair localKeyPair = records.getLocalKeyRecord().getKeyPairForId(localKeyId).getKeyPair();
+ ECPublicKeyParameters remoteKey = records.getRemoteKeyRecord().getKeyForId(remoteKeyId).getKey();
agreement.init(localKeyPair.getPrivate());
- BigInteger secret = KeyUtil.calculateAgreement(agreement, remoteKey);
-
- return secret;
+
+ return KeyUtil.calculateAgreement(agreement, remoteKey);
}
+
+ private KeyRecords getKeyRecords(Context context, MasterSecret masterSecret,
+ CanonicalRecipientAddress recipient)
+ {
+ LocalKeyRecord localKeyRecord = new LocalKeyRecord(context, masterSecret, recipient);
+ RemoteKeyRecord remoteKeyRecord = new RemoteKeyRecord(context, recipient);
+ SessionRecord sessionRecord = new SessionRecord(context, masterSecret, recipient);
+ return new KeyRecords(localKeyRecord, remoteKeyRecord, sessionRecord);
+ }
+
+ private static class KeyRecords {
+ private final LocalKeyRecord localKeyRecord;
+ private final RemoteKeyRecord remoteKeyRecord;
+ private final SessionRecord sessionRecord;
+
+ public KeyRecords(LocalKeyRecord localKeyRecord, RemoteKeyRecord remoteKeyRecord, SessionRecord sessionRecord) {
+ this.localKeyRecord = localKeyRecord;
+ this.remoteKeyRecord = remoteKeyRecord;
+ this.sessionRecord = sessionRecord;
+ }
+
+ private LocalKeyRecord getLocalKeyRecord() {
+ return localKeyRecord;
+ }
+
+ private RemoteKeyRecord getRemoteKeyRecord() {
+ return remoteKeyRecord;
+ }
+
+ private SessionRecord getSessionRecord() {
+ return sessionRecord;
+ }
+ }
+
+ public static class SessionCipherContext {
+ private final LocalKeyRecord localKeyRecord;
+ private final RemoteKeyRecord remoteKeyRecord;
+ private final SessionRecord sessionRecord;
+ private final SessionKey sessionKey;
+ private final int senderKeyId;
+ private final int recipientKeyId;
+ private final PublicKey nextKey;
+ private final int counter;
+ private final int negotiatedVersion;
+
+ public SessionCipherContext(KeyRecords records,
+ SessionKey sessionKey,
+ int senderKeyId,
+ int receiverKeyId,
+ PublicKey nextKey,
+ int counter,
+ int negotiatedVersion)
+ {
+ this.localKeyRecord = records.getLocalKeyRecord();
+ this.remoteKeyRecord = records.getRemoteKeyRecord();
+ this.sessionRecord = records.getSessionRecord();
+ this.sessionKey = sessionKey;
+ this.senderKeyId = senderKeyId;
+ this.recipientKeyId = receiverKeyId;
+ this.nextKey = nextKey;
+ this.counter = counter;
+ this.negotiatedVersion = negotiatedVersion;
+ }
+
+ public LocalKeyRecord getLocalKeyRecord() {
+ return localKeyRecord;
+ }
+
+ public RemoteKeyRecord getRemoteKeyRecord() {
+ return remoteKeyRecord;
+ }
+
+ public SessionRecord getSessionRecord() {
+ return sessionRecord;
+ }
+
+ public SessionKey getSessionKey() {
+ return sessionKey;
+ }
+
+ public PublicKey getNextKey() {
+ return nextKey;
+ }
+
+ public int getCounter() {
+ return counter;
+ }
+
+ public int getSenderKeyId() {
+ return senderKeyId;
+ }
+
+ public int getRecipientKeyId() {
+ return recipientKeyId;
+ }
+
+ public int getNegotiatedVersion() {
+ return negotiatedVersion;
+ }
+ }
+
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/TransportDetails.java b/library/src/org/whispersystems/textsecure/crypto/TransportDetails.java
index b03398f9bc..9ecd0ced62 100644
--- a/library/src/org/whispersystems/textsecure/crypto/TransportDetails.java
+++ b/library/src/org/whispersystems/textsecure/crypto/TransportDetails.java
@@ -20,9 +20,9 @@ import java.io.IOException;
public interface TransportDetails {
- public byte[] stripPaddedMessage(byte[] messageWithPadding);
+ public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding);
public byte[] getPaddedMessageBody(byte[] messageBody);
- public byte[] encodeMessage(byte[] messageWithMac);
- public byte[] decodeMessage(byte[] encodedMessageBytes) throws IOException;
+ public byte[] getEncodedMessage(byte[] messageWithMac);
+ public byte[] getDecodedMessage(byte[] encodedMessageBytes) throws IOException;
}
diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/EncryptedMessage.java b/library/src/org/whispersystems/textsecure/crypto/protocol/EncryptedMessage.java
new file mode 100644
index 0000000000..13ad15ecbc
--- /dev/null
+++ b/library/src/org/whispersystems/textsecure/crypto/protocol/EncryptedMessage.java
@@ -0,0 +1,170 @@
+/**
+ * Copyright (C) 2011 Whisper Systems
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package org.whispersystems.textsecure.crypto.protocol;
+
+import android.content.Context;
+
+import org.whispersystems.textsecure.crypto.InvalidKeyException;
+import org.whispersystems.textsecure.crypto.InvalidMessageException;
+import org.whispersystems.textsecure.crypto.MasterSecret;
+import org.whispersystems.textsecure.crypto.PublicKey;
+import org.whispersystems.textsecure.crypto.SessionCipher;
+import org.whispersystems.textsecure.crypto.SessionCipher.SessionCipherContext;
+import org.whispersystems.textsecure.crypto.TransportDetails;
+import org.whispersystems.textsecure.storage.CanonicalRecipientAddress;
+import org.whispersystems.textsecure.util.Conversions;
+
+import java.io.IOException;
+import java.nio.ByteBuffer;
+
+/**
+ * Parses and serializes the encrypted message format.
+ *
+ * @author Moxie Marlinspike
+ */
+
+public class EncryptedMessage {
+
+ public static final int SUPPORTED_VERSION = 1;
+
+ private static final int VERSION_LENGTH = 1;
+ private static final int SENDER_KEY_ID_LENGTH = 3;
+ private static final int RECEIVER_KEY_ID_LENGTH = 3;
+ private static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE;
+ private static final int COUNTER_LENGTH = 3;
+ public static final int HEADER_LENGTH = VERSION_LENGTH + SENDER_KEY_ID_LENGTH + RECEIVER_KEY_ID_LENGTH + COUNTER_LENGTH + NEXT_KEY_LENGTH;
+
+ private static final int VERSION_OFFSET = 0;
+ private static final int SENDER_KEY_ID_OFFSET = VERSION_OFFSET + VERSION_LENGTH;
+ private static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH;
+ private static final int NEXT_KEY_OFFSET = RECEIVER_KEY_ID_OFFSET + RECEIVER_KEY_ID_LENGTH;
+ private static final int COUNTER_OFFSET = NEXT_KEY_OFFSET + NEXT_KEY_LENGTH;
+ private static final int TEXT_OFFSET = COUNTER_OFFSET + COUNTER_LENGTH;
+
+ private final Context context;
+ private final MasterSecret masterSecret;
+ private final TransportDetails transportDetails;
+
+ public EncryptedMessage(Context context, MasterSecret masterSecret, TransportDetails transportDetails) {
+ this.context = context.getApplicationContext();
+ this.masterSecret = masterSecret;
+ this.transportDetails = transportDetails;
+ }
+
+ public byte[] encrypt(CanonicalRecipientAddress recipient, byte[] plaintext) {
+ synchronized (SessionCipher.CIPHER_LOCK) {
+ byte[] paddedBody = transportDetails.getPaddedMessageBody(plaintext);
+ SessionCipher sessionCipher = new SessionCipher();
+ SessionCipherContext sessionContext = sessionCipher.getEncryptionContext(context, masterSecret, recipient);
+ byte[] ciphertextBody = sessionCipher.encrypt(sessionContext, paddedBody);
+ byte[] formattedCiphertext = getFormattedCiphertext(sessionContext, ciphertextBody);
+ byte[] ciphertextMessage = sessionCipher.mac(sessionContext, formattedCiphertext);
+
+ return transportDetails.getEncodedMessage(ciphertextMessage);
+ }
+ }
+
+ public byte[] decrypt(CanonicalRecipientAddress recipient, byte[] ciphertext)
+ throws InvalidMessageException
+ {
+ synchronized (SessionCipher.CIPHER_LOCK) {
+ try {
+ byte[] decodedMessage = transportDetails.getDecodedMessage(ciphertext);
+ int messageVersion = getMessageVersion(decodedMessage);
+
+ if (messageVersion > SUPPORTED_VERSION) {
+ throw new InvalidMessageException("Unsupported version: " + messageVersion);
+ }
+
+ int supportedVersion = getSupportedVersion(decodedMessage);
+ int receiverKeyId = getReceiverKeyId(decodedMessage);
+ int senderKeyId = getSenderKeyId(decodedMessage);
+ int counter = getCiphertextCounter(decodedMessage);
+ byte[] ciphertextBody = getMessageBody(decodedMessage);
+ PublicKey nextRemoteKey = getNextRemoteKey(decodedMessage);
+ int version = Math.min(supportedVersion, SUPPORTED_VERSION);
+ SessionCipher sessionCipher = new SessionCipher();
+ SessionCipherContext sessionContext = sessionCipher.getDecryptionContext(context, masterSecret,
+ recipient, senderKeyId,
+ receiverKeyId,
+ nextRemoteKey,
+ counter, version);
+
+ sessionCipher.verifyMac(sessionContext, decodedMessage);
+
+ byte[] plaintextWithPadding = sessionCipher.decrypt(sessionContext, ciphertextBody);
+ return transportDetails.getStrippedPaddingMessageBody(plaintextWithPadding);
+ } catch (IOException e) {
+ throw new InvalidMessageException(e);
+ } catch (InvalidKeyException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+ }
+
+ private byte[] getFormattedCiphertext(SessionCipherContext sessionContext, byte[] ciphertextBody) {
+ ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + ciphertextBody.length);
+ byte versionByte = Conversions.intsToByteHighAndLow(sessionContext.getNegotiatedVersion(), SUPPORTED_VERSION);
+ byte[] senderKeyIdBytes = Conversions.mediumToByteArray(sessionContext.getSenderKeyId());
+ byte[] receiverKeyIdBytes = Conversions.mediumToByteArray(sessionContext.getRecipientKeyId());
+ byte[] nextKeyBytes = sessionContext.getNextKey().serialize();
+ byte[] counterBytes = Conversions.mediumToByteArray(sessionContext.getCounter());
+
+ buffer.put(versionByte);
+ buffer.put(senderKeyIdBytes);
+ buffer.put(receiverKeyIdBytes);
+ buffer.put(nextKeyBytes);
+ buffer.put(counterBytes);
+ buffer.put(ciphertextBody);
+
+ return buffer.array();
+ }
+
+ private int getMessageVersion(byte[] message) {
+ return Conversions.highBitsToInt(message[VERSION_OFFSET]);
+ }
+
+ private int getSupportedVersion(byte[] message) {
+ return Conversions.lowBitsToInt(message[VERSION_OFFSET]);
+ }
+
+ private int getSenderKeyId(byte[] message) {
+ return Conversions.byteArrayToMedium(message, SENDER_KEY_ID_OFFSET);
+ }
+
+ private int getReceiverKeyId(byte[] message) {
+ return Conversions.byteArrayToMedium(message, RECEIVER_KEY_ID_OFFSET);
+ }
+
+ private int getCiphertextCounter(byte[] message) {
+ return Conversions.byteArrayToMedium(message, COUNTER_OFFSET);
+ }
+
+ private byte[] getMessageBody(byte[] message) {
+ byte[] body = new byte[message.length - HEADER_LENGTH];
+ System.arraycopy(message, TEXT_OFFSET, body, 0, body.length);
+
+ return body;
+ }
+
+ private PublicKey getNextRemoteKey(byte[] message) throws InvalidKeyException {
+ byte[] key = new byte[NEXT_KEY_LENGTH];
+ System.arraycopy(message, NEXT_KEY_OFFSET, key, 0, key.length);
+
+ return new PublicKey(key);
+ }
+}
diff --git a/library/src/org/whispersystems/textsecure/crypto/protocol/Message.java b/library/src/org/whispersystems/textsecure/crypto/protocol/Message.java
deleted file mode 100644
index 709d943e9c..0000000000
--- a/library/src/org/whispersystems/textsecure/crypto/protocol/Message.java
+++ /dev/null
@@ -1,151 +0,0 @@
-/**
- * Copyright (C) 2011 Whisper Systems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.whispersystems.textsecure.crypto.protocol;
-
-import android.util.Log;
-
-import org.whispersystems.textsecure.crypto.InvalidKeyException;
-import org.whispersystems.textsecure.crypto.InvalidMessageException;
-import org.whispersystems.textsecure.crypto.PublicKey;
-import org.whispersystems.textsecure.util.Conversions;
-
-import java.nio.ByteBuffer;
-
-/**
- * Parses and serializes the encrypted message format.
- *
- * @author Moxie Marlinspike
- */
-
-public class Message {
-
- public static final int SUPPORTED_VERSION = 1;
-
- private static final int VERSION_LENGTH = 1;
- private static final int SENDER_KEY_ID_LENGTH = 3;
- private static final int RECEIVER_KEY_ID_LENGTH = 3;
- private static final int NEXT_KEY_LENGTH = PublicKey.KEY_SIZE;
- private static final int COUNTER_LENGTH = 3;
- public static final int HEADER_LENGTH = VERSION_LENGTH + SENDER_KEY_ID_LENGTH + RECEIVER_KEY_ID_LENGTH + COUNTER_LENGTH + NEXT_KEY_LENGTH;
-
- private static final int VERSION_OFFSET = 0;
- private static final int SENDER_KEY_ID_OFFSET = VERSION_OFFSET + VERSION_LENGTH;
- private static final int RECEIVER_KEY_ID_OFFSET = SENDER_KEY_ID_OFFSET + SENDER_KEY_ID_LENGTH;
- private static final int NEXT_KEY_OFFSET = RECEIVER_KEY_ID_OFFSET + RECEIVER_KEY_ID_LENGTH;
- private static final int COUNTER_OFFSET = NEXT_KEY_OFFSET + NEXT_KEY_LENGTH;
- private static final int TEXT_OFFSET = COUNTER_OFFSET + COUNTER_LENGTH;
-
- private int senderKeyId;
- private int receiverKeyId;
- private int counter;
- private int messageVersion;
- private int supportedVersion;
- private byte[] message;
-
- private PublicKey nextKey;
-
- public Message(int senderKeyId, int receiverKeyId, PublicKey nextKey, int counter, byte[] message, int messageVersion, int supportedVersion) {
- this.senderKeyId = senderKeyId;
- this.receiverKeyId = receiverKeyId;
- this.nextKey = nextKey;
- this.counter = counter;
- this.message = message;
- this.messageVersion = messageVersion;
- this.supportedVersion = supportedVersion;
- }
-
- public Message(byte[] messageBytes) throws InvalidMessageException {
- try {
- if (messageBytes.length <= HEADER_LENGTH)
- throw new InvalidMessageException("Message is shorter than headers.");
-
- this.messageVersion = Conversions.highBitsToInt(messageBytes[VERSION_OFFSET]);
- this.supportedVersion = Conversions.lowBitsToInt(messageBytes[VERSION_OFFSET]);
-
- Log.w("Message", "Message Version: " + messageVersion);
- Log.w("Message", "Supported Version: " + supportedVersion);
-
- if (messageVersion > SUPPORTED_VERSION)
- throw new InvalidMessageException("Message protocol version not supported: " + messageVersion);
-
- this.senderKeyId = Conversions.byteArrayToMedium(messageBytes, SENDER_KEY_ID_OFFSET);
- this.receiverKeyId = Conversions.byteArrayToMedium(messageBytes, RECEIVER_KEY_ID_OFFSET);
- this.counter = Conversions.byteArrayToMedium(messageBytes, COUNTER_OFFSET);
-
- Log.w("Message", "Parsed current version: " + messageVersion + " supported version: " + supportedVersion);
-
- byte[] nextKeyBytes = new byte[NEXT_KEY_LENGTH];
- byte[] textBytes = new byte[messageBytes.length - HEADER_LENGTH];
-
- System.arraycopy(messageBytes, NEXT_KEY_OFFSET, nextKeyBytes, 0, nextKeyBytes.length);
- System.arraycopy(messageBytes, TEXT_OFFSET, textBytes, 0, textBytes.length);
-
- Log.w("Message", "Pulling next key out of message...");
- this.nextKey = new PublicKey(nextKeyBytes);
- this.message = textBytes;
- } catch (InvalidKeyException ike) {
- throw new AssertionError(ike);
- }
- }
-
- public byte[] serialize() {
- ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + message.length);
-
- Log.w("Message", "Constructing Message Version: (" + messageVersion + "," + supportedVersion + ")");
-
- byte versionByte = Conversions.intsToByteHighAndLow(messageVersion, supportedVersion);
- byte[] senderKeyIdBytes = Conversions.mediumToByteArray(senderKeyId);
- byte[] receiverKeyIdBytes = Conversions.mediumToByteArray(receiverKeyId);
- Log.w("Message", "Serializing next key into message...");
- byte[] nextKeyBytes = nextKey.serialize();
- byte[] counterBytes = Conversions.mediumToByteArray(counter);
-
- buffer.put(versionByte);
- buffer.put(senderKeyIdBytes);
- buffer.put(receiverKeyIdBytes);
- buffer.put(nextKeyBytes);
- buffer.put(counterBytes);
- buffer.put(message);
-
- return buffer.array();
- }
-
- public int getHighestMutuallySupportedVersion() {
- return Math.min(SUPPORTED_VERSION, this.supportedVersion);
- }
-
- public int getSenderKeyId() {
- return this.senderKeyId;
- }
-
- public int getReceiverKeyId() {
- return this.receiverKeyId;
- }
-
- public PublicKey getNextKey() {
- return this.nextKey;
- }
-
- public int getCounter() {
- return this.counter;
- }
-
- public byte[] getMessageText() {
- return this.message;
- }
-
-}
diff --git a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
index d64c776e14..8f50d0c799 100644
--- a/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
+++ b/src/org/thoughtcrime/securesms/crypto/DecryptingQueue.java
@@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.sms.SmsTransportDetails;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.InvalidMessageException;
import org.whispersystems.textsecure.crypto.SessionCipher;
+import org.whispersystems.textsecure.crypto.protocol.EncryptedMessage;
import org.whispersystems.textsecure.util.Hex;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.WorkerThread;
@@ -194,9 +195,10 @@ public class DecryptingQueue {
synchronized (SessionCipher.CIPHER_LOCK) {
Log.w("DecryptingQueue", "Decrypting: " + Hex.toString(ciphertextPduBytes));
- SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new TextTransport());
+ EncryptedMessage message = new EncryptedMessage(context, masterSecret, new TextTransport());
+
try {
- plaintextPduBytes = cipher.decryptMessage(ciphertextPduBytes);
+ plaintextPduBytes = message.decrypt(recipient, ciphertextPduBytes);
} catch (InvalidMessageException ime) {
// XXX - For some reason, Sprint seems to append a single character to the
// end of message text segments. I don't know why, so here we just try
@@ -205,7 +207,7 @@ public class DecryptingQueue {
Log.w("DecryptingQueue", "Attempting truncated decrypt...");
byte[] truncated = new byte[ciphertextPduBytes.length - 1];
System.arraycopy(ciphertextPduBytes, 0, truncated, 0, truncated.length);
- plaintextPduBytes = cipher.decryptMessage(truncated);
+ plaintextPduBytes = message.decrypt(recipient, truncated);
} else {
throw ime;
}
@@ -273,8 +275,8 @@ public class DecryptingQueue {
return;
}
- SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new SmsTransportDetails());
- plaintextBody = new String(cipher.decryptMessage(body.getBytes()));
+ EncryptedMessage message = new EncryptedMessage(context, masterSecret, new SmsTransportDetails());
+ plaintextBody = new String(message.decrypt(recipient, body.getBytes()));
} catch (InvalidMessageException e) {
Log.w("DecryptionQueue", e);
database.markAsDecryptFailed(messageId);
diff --git a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
index 82b39ab16b..7c02a67fa4 100644
--- a/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
+++ b/src/org/thoughtcrime/securesms/crypto/KeyExchangeProcessor.java
@@ -26,11 +26,11 @@ import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.KeyUtil;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey;
+import org.whispersystems.textsecure.crypto.protocol.EncryptedMessage;
import org.whispersystems.textsecure.push.PreKeyEntity;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
import org.whispersystems.textsecure.storage.RemoteKeyRecord;
import org.whispersystems.textsecure.storage.SessionRecord;
-import org.whispersystems.textsecure.crypto.protocol.Message;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.sms.MessageSender;
@@ -105,7 +105,7 @@ public class KeyExchangeProcessor {
sessionRecord.setSessionId(localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprintBytes(),
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
sessionRecord.setIdentityKey(message.getIdentityKey());
- sessionRecord.setSessionVersion(Message.SUPPORTED_VERSION);
+ sessionRecord.setSessionVersion(EncryptedMessage.SUPPORTED_VERSION);
sessionRecord.save();
DatabaseFactory.getIdentityDatabase(context)
@@ -118,7 +118,7 @@ public class KeyExchangeProcessor {
if (needsResponseFromUs()) {
localKeyRecord = KeyUtil.initializeRecordFor(recipient, context, masterSecret);
- KeyExchangeMessage ourMessage = new KeyExchangeMessage(context, masterSecret, Math.min(Message.SUPPORTED_VERSION, message.getMaxVersion()), localKeyRecord, initiateKeyId);
+ KeyExchangeMessage ourMessage = new KeyExchangeMessage(context, masterSecret, Math.min(EncryptedMessage.SUPPORTED_VERSION, message.getMaxVersion()), localKeyRecord, initiateKeyId);
OutgoingKeyExchangeMessage textMessage = new OutgoingKeyExchangeMessage(recipient, ourMessage.serialize());
Log.w("KeyExchangeProcessor", "Responding with key exchange message fingerprint: " + ourMessage.getPublicKey().getFingerprint());
Log.w("KeyExchangeProcessor", "Which has a local key record fingerprint: " + localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprint());
@@ -132,9 +132,9 @@ public class KeyExchangeProcessor {
sessionRecord.setSessionId(localKeyRecord.getCurrentKeyPair().getPublicKey().getFingerprintBytes(),
remoteKeyRecord.getCurrentRemoteKey().getFingerprintBytes());
sessionRecord.setIdentityKey(message.getIdentityKey());
- sessionRecord.setSessionVersion(Math.min(Message.SUPPORTED_VERSION, message.getMaxVersion()));
+ sessionRecord.setSessionVersion(Math.min(EncryptedMessage.SUPPORTED_VERSION, message.getMaxVersion()));
- Log.w("KeyExchangeUtil", "Setting session version: " + Math.min(Message.SUPPORTED_VERSION, message.getMaxVersion()));
+ Log.w("KeyExchangeUtil", "Setting session version: " + Math.min(EncryptedMessage.SUPPORTED_VERSION, message.getMaxVersion()));
sessionRecord.save();
diff --git a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
index c08312e4d0..a38bde8c3b 100644
--- a/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
+++ b/src/org/thoughtcrime/securesms/crypto/protocol/KeyExchangeMessage.java
@@ -25,8 +25,8 @@ import org.whispersystems.textsecure.crypto.IdentityKey;
import org.whispersystems.textsecure.crypto.InvalidKeyException;
import org.whispersystems.textsecure.crypto.MasterSecret;
import org.whispersystems.textsecure.crypto.PublicKey;
+import org.whispersystems.textsecure.crypto.protocol.EncryptedMessage;
import org.whispersystems.textsecure.storage.LocalKeyRecord;
-import org.whispersystems.textsecure.crypto.protocol.Message;
import org.whispersystems.textsecure.util.Base64;
import org.whispersystems.textsecure.util.Conversions;
@@ -67,7 +67,7 @@ public class KeyExchangeMessage {
public KeyExchangeMessage(Context context, MasterSecret masterSecret, int messageVersion, LocalKeyRecord record, int highIdBits) {
this.publicKey = new PublicKey(record.getCurrentKeyPair().getPublicKey());
this.messageVersion = messageVersion;
- this.supportedVersion = Message.SUPPORTED_VERSION;
+ this.supportedVersion = EncryptedMessage.SUPPORTED_VERSION;
publicKey.setId(publicKey.getId() | (highIdBits << 12));
@@ -93,9 +93,9 @@ public class KeyExchangeMessage {
this.supportedVersion = Conversions.lowBitsToInt(keyBytes[0]);
this.serialized = messageBody;
- if (messageVersion > Message.SUPPORTED_VERSION)
+ if (messageVersion > EncryptedMessage.SUPPORTED_VERSION)
throw new InvalidVersionException("Key exchange with version: " + messageVersion +
- " but we only support: " + Message.SUPPORTED_VERSION);
+ " but we only support: " + EncryptedMessage.SUPPORTED_VERSION);
if (messageVersion >= 1)
keyBytes = Base64.decodeWithoutPadding(messageBody);
diff --git a/src/org/thoughtcrime/securesms/mms/PngTransport.java b/src/org/thoughtcrime/securesms/mms/PngTransport.java
deleted file mode 100644
index 923e9ec1fc..0000000000
--- a/src/org/thoughtcrime/securesms/mms/PngTransport.java
+++ /dev/null
@@ -1,207 +0,0 @@
-/**
- * Copyright (C) 2011 Whisper Systems
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package org.thoughtcrime.securesms.mms;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.zip.CRC32;
-import java.util.zip.DeflaterOutputStream;
-import java.util.zip.InflaterInputStream;
-
-import org.whispersystems.textsecure.crypto.SessionCipher;
-import org.whispersystems.textsecure.crypto.TransportDetails;
-import org.whispersystems.textsecure.util.Conversions;
-
-import android.util.Log;
-
-public class PngTransport implements TransportDetails {
-
- private static final int CRC_LENGTH = 4;
- private static final int IHDR_WIDTH_OFFSET = 8;
- private static final int IHDR_HEIGHT_OFFSET = 12;
- private static final int IDAT_HEADER_LENGTH = 8;
-
- private static final byte[] PNG_HEADER = {(byte)0x89, (byte)0x50, (byte)0x4E, (byte)0x47, (byte)0x0D, (byte)0x0A, (byte)0x1A, (byte)0x0A};
- private static final byte[] IHDR_CHUNK = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x0D, (byte)0x49, (byte)0x48, (byte)0x44, (byte)0x52,
- (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
- (byte)0x10, (byte)0x06, (byte)0x00, (byte)0x00, (byte)0x00};
- private static final byte[] IEND_CHUNK = {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x49, (byte)0x45, (byte)0x4E, (byte)0x44};
- private static final byte[] IDAT_TAG = {(byte)0x49, (byte)0x44, (byte)0x41, (byte)0x54};
-
- private static final int BYTES_PER_ROW = 640 * 8;
- private static final int IDAT_LENGTH_OFFSET = 33;
-
- private void readFully(InputStream in, byte[] buffer) throws IOException {
- int totalRead = 0;
- int read = 0;
-
- while (totalRead < buffer.length) {
- read = in.read(buffer, totalRead, buffer.length-totalRead);
- Log.w("PngTransport", "Read: " + read);
- if (read == -1)
- throw new IOException("Could not fill buffer!");
- totalRead+=read;
- }
- }
-
- private int getImageHeight(byte[] data) {
- int rows = (data.length / BYTES_PER_ROW);
- assert(data.length % BYTES_PER_ROW == 0);
- return rows;
- }
-
- private byte[] getIhdr(int width, int height) {
- byte[] ihdr = new byte[IHDR_CHUNK.length];
- System.arraycopy(IHDR_CHUNK, 0, ihdr, 0, ihdr.length);
- Conversions.intToByteArray(ihdr, IHDR_WIDTH_OFFSET, width);
- Conversions.intToByteArray(ihdr, IHDR_HEIGHT_OFFSET, height);
- return ihdr;
- }
-
- private byte[] calculateChunkCrc(byte[] header, byte[] chunk) {
- byte[] crcBytes = new byte[4];
- CRC32 crc = new CRC32();
- crc.update(header, 4, header.length - 4);
-
- if (chunk != null)
- crc.update(chunk, 0, chunk.length);
-
- long crcValue = crc.getValue();
- Conversions.longTo4ByteArray(crcBytes, 0, crcValue);
-
- return crcBytes;
- }
-
- private int readIhdr(ByteArrayInputStream bais) throws IOException {
- byte[] ihdr = new byte[IHDR_CHUNK.length];
- bais.read(ihdr);
- return Conversions.byteArrayToInt(ihdr, IHDR_HEIGHT_OFFSET);
- }
-
- private byte[] readIdatChunk(ByteArrayInputStream bais, int rows) throws IOException {
- InflaterInputStream iis = new InflaterInputStream(bais);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- byte[] buffer = new byte[BYTES_PER_ROW + 1];
-
- Log.w("PngTransport", "Total image height: " + rows);
-
- for (int i=0;i=0;i--) {
- if (messageWithPadding[i] == (byte)0x01)
- break;
- else if (i == 0)
- throw new AssertionError("No padding!");
- }
-
- int paddingLength = messageWithPadding.length - i;
- byte[] message = new byte[messageWithPadding.length - paddingLength];
-
- System.arraycopy(messageWithPadding, 0, message, 0, message.length);
- return message;
- }
-}
diff --git a/src/org/thoughtcrime/securesms/mms/TextTransport.java b/src/org/thoughtcrime/securesms/mms/TextTransport.java
index ad18fd3043..f1141ca371 100644
--- a/src/org/thoughtcrime/securesms/mms/TextTransport.java
+++ b/src/org/thoughtcrime/securesms/mms/TextTransport.java
@@ -23,19 +23,23 @@ import org.whispersystems.textsecure.util.Base64;
public class TextTransport implements TransportDetails {
- public byte[] decodeMessage(byte[] encodedMessageBytes) throws IOException {
+ @Override
+ public byte[] getDecodedMessage(byte[] encodedMessageBytes) throws IOException {
return Base64.decode(encodedMessageBytes);
}
- public byte[] encodeMessage(byte[] messageWithMac) {
+ @Override
+ public byte[] getEncodedMessage(byte[] messageWithMac) {
return Base64.encodeBytes(messageWithMac).getBytes();
}
+ @Override
public byte[] getPaddedMessageBody(byte[] messageBody) {
return messageBody;
}
- public byte[] stripPaddedMessage(byte[] messageWithPadding) {
+ @Override
+ public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) {
return messageWithPadding;
}
diff --git a/src/org/thoughtcrime/securesms/sms/SmsTransportDetails.java b/src/org/thoughtcrime/securesms/sms/SmsTransportDetails.java
index 0a7283a9fc..9a835994d6 100644
--- a/src/org/thoughtcrime/securesms/sms/SmsTransportDetails.java
+++ b/src/org/thoughtcrime/securesms/sms/SmsTransportDetails.java
@@ -38,20 +38,20 @@ public class SmsTransportDetails implements TransportDetails {
public static final int ENCRYPTED_SINGLE_MESSAGE_BODY_MAX_SIZE = SINGLE_MESSAGE_MAX_BYTES - SessionCipher.ENCRYPTED_MESSAGE_OVERHEAD;
@Override
- public byte[] encodeMessage(byte[] messageWithMac) {
+ public byte[] getEncodedMessage(byte[] messageWithMac) {
String encodedMessage = Base64.encodeBytesWithoutPadding(messageWithMac);
Log.w("SmsTransportDetails", "Encoded Message Length: " + encodedMessage.length());
return encodedMessage.getBytes();
}
@Override
- public byte[] decodeMessage(byte[] encodedMessageBytes) throws IOException {
+ public byte[] getDecodedMessage(byte[] encodedMessageBytes) throws IOException {
String encodedMessage = new String(encodedMessageBytes);
return Base64.decodeWithoutPadding(encodedMessage);
}
@Override
- public byte[] stripPaddedMessage(byte[] messageWithPadding) {
+ public byte[] getStrippedPaddingMessageBody(byte[] messageWithPadding) {
int paddingBeginsIndex = 0;
for (int i=1;i