2012-09-30 19:56:29 -07:00
|
|
|
/**
|
2011-12-20 10:20:44 -08:00
|
|
|
* Copyright (C) 2011 Whisper Systems
|
2012-09-30 19:56:29 -07:00
|
|
|
*
|
2011-12-20 10:20:44 -08:00
|
|
|
* 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.
|
2012-09-30 19:56:29 -07:00
|
|
|
*
|
2011-12-20 10:20:44 -08:00
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
package org.thoughtcrime.securesms.protocol;
|
|
|
|
|
2012-09-30 19:56:29 -07:00
|
|
|
import android.util.Log;
|
2011-12-20 10:20:44 -08:00
|
|
|
|
|
|
|
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
|
|
|
|
import org.thoughtcrime.securesms.crypto.InvalidMessageException;
|
|
|
|
import org.thoughtcrime.securesms.crypto.PublicKey;
|
|
|
|
import org.thoughtcrime.securesms.util.Conversions;
|
|
|
|
|
2012-09-30 19:56:29 -07:00
|
|
|
import java.nio.ByteBuffer;
|
2011-12-20 10:20:44 -08:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses and serializes the encrypted message format.
|
2012-09-30 19:56:29 -07:00
|
|
|
*
|
2011-12-20 10:20:44 -08:00
|
|
|
* @author Moxie Marlinspike
|
|
|
|
*/
|
|
|
|
|
|
|
|
public class Message {
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public static final int SUPPORTED_VERSION = 1;
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
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;
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
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;
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
private PublicKey nextKey;
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
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;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public Message(byte[] messageBytes) throws InvalidMessageException {
|
|
|
|
try {
|
|
|
|
if (messageBytes.length <= HEADER_LENGTH)
|
|
|
|
throw new InvalidMessageException("Message is shorter than headers.");
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
this.messageVersion = Conversions.highBitsToInt(messageBytes[VERSION_OFFSET]);
|
|
|
|
this.supportedVersion = Conversions.lowBitsToInt(messageBytes[VERSION_OFFSET]);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
Log.w("Message", "Message Version: " + messageVersion);
|
|
|
|
Log.w("Message", "Supported Version: " + supportedVersion);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
if (messageVersion > SUPPORTED_VERSION)
|
|
|
|
throw new InvalidMessageException("Message protocol version not supported: " + messageVersion);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
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);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
Log.w("Message", "Parsed current version: " + messageVersion + " supported version: " + supportedVersion);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
byte[] nextKeyBytes = new byte[NEXT_KEY_LENGTH];
|
|
|
|
byte[] textBytes = new byte[messageBytes.length - HEADER_LENGTH];
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
System.arraycopy(messageBytes, NEXT_KEY_OFFSET, nextKeyBytes, 0, nextKeyBytes.length);
|
|
|
|
System.arraycopy(messageBytes, TEXT_OFFSET, textBytes, 0, textBytes.length);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
Log.w("Message", "Pulling next key out of message...");
|
|
|
|
this.nextKey = new PublicKey(nextKeyBytes);
|
|
|
|
this.message = textBytes;
|
|
|
|
} catch (InvalidKeyException ike) {
|
|
|
|
throw new AssertionError(ike);
|
|
|
|
}
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public byte[] serialize() {
|
|
|
|
ByteBuffer buffer = ByteBuffer.allocate(HEADER_LENGTH + message.length);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
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);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
buffer.put(versionByte);
|
|
|
|
buffer.put(senderKeyIdBytes);
|
|
|
|
buffer.put(receiverKeyIdBytes);
|
|
|
|
buffer.put(nextKeyBytes);
|
|
|
|
buffer.put(counterBytes);
|
|
|
|
buffer.put(message);
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
return buffer.array();
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public int getHighestMutuallySupportedVersion() {
|
|
|
|
return Math.min(SUPPORTED_VERSION, this.supportedVersion);
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public int getSenderKeyId() {
|
|
|
|
return this.senderKeyId;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public int getReceiverKeyId() {
|
|
|
|
return this.receiverKeyId;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public PublicKey getNextKey() {
|
|
|
|
return this.nextKey;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public int getCounter() {
|
|
|
|
return this.counter;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
public byte[] getMessageText() {
|
|
|
|
return this.message;
|
|
|
|
}
|
2012-09-30 19:56:29 -07:00
|
|
|
|
2011-12-20 10:20:44 -08:00
|
|
|
}
|