aboutsummaryrefslogtreecommitdiffstats
path: root/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java121
1 files changed, 121 insertions, 0 deletions
diff --git a/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java
new file mode 100644
index 00000000..b3a17456
--- /dev/null
+++ b/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyMessage.java
@@ -0,0 +1,121 @@
+package org.whispersystems.libaxolotl.protocol;
+
+import com.google.protobuf.ByteString;
+import com.google.protobuf.InvalidProtocolBufferException;
+
+import org.whispersystems.libaxolotl.InvalidKeyException;
+import org.whispersystems.libaxolotl.InvalidMessageException;
+import org.whispersystems.libaxolotl.LegacyMessageException;
+import org.whispersystems.libaxolotl.ecc.Curve;
+import org.whispersystems.libaxolotl.ecc.ECPrivateKey;
+import org.whispersystems.libaxolotl.ecc.ECPublicKey;
+import org.whispersystems.libaxolotl.util.ByteUtil;
+
+import java.text.ParseException;
+
+public class SenderKeyMessage implements CiphertextMessage {
+
+ private static final int SIGNATURE_LENGTH = 64;
+
+ private final int messageVersion;
+ private final int keyId;
+ private final int iteration;
+ private final byte[] ciphertext;
+ private final byte[] serialized;
+
+ public SenderKeyMessage(byte[] serialized) throws InvalidMessageException, LegacyMessageException {
+ try {
+ byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1 - SIGNATURE_LENGTH, SIGNATURE_LENGTH);
+ byte version = messageParts[0][0];
+ byte[] message = messageParts[1];
+ byte[] signature = messageParts[2];
+
+ if (ByteUtil.highBitsToInt(version) < 3) {
+ throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version));
+ }
+
+ if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) {
+ throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version));
+ }
+
+ WhisperProtos.SenderKeyMessage senderKeyMessage = WhisperProtos.SenderKeyMessage.parseFrom(message);
+
+ if (!senderKeyMessage.hasId() ||
+ !senderKeyMessage.hasIteration() ||
+ !senderKeyMessage.hasCiphertext())
+ {
+ throw new InvalidMessageException("Incomplete message.");
+ }
+
+ this.serialized = serialized;
+ this.messageVersion = ByteUtil.highBitsToInt(version);
+ this.keyId = senderKeyMessage.getId();
+ this.iteration = senderKeyMessage.getIteration();
+ this.ciphertext = senderKeyMessage.getCiphertext().toByteArray();
+ } catch (InvalidProtocolBufferException | ParseException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+ public SenderKeyMessage(int keyId, int iteration, byte[] ciphertext, ECPrivateKey signatureKey) {
+ byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)};
+ byte[] message = WhisperProtos.SenderKeyMessage.newBuilder()
+ .setId(keyId)
+ .setIteration(iteration)
+ .setCiphertext(ByteString.copyFrom(ciphertext))
+ .build().toByteArray();
+
+ byte[] signature = getSignature(signatureKey, ByteUtil.combine(version, message));
+
+ this.serialized = ByteUtil.combine(version, message, signature);
+ this.messageVersion = CURRENT_VERSION;
+ this.keyId = keyId;
+ this.iteration = iteration;
+ this.ciphertext = ciphertext;
+ }
+
+ public int getKeyId() {
+ return keyId;
+ }
+
+ public int getIteration() {
+ return iteration;
+ }
+
+ public byte[] getCipherText() {
+ return ciphertext;
+ }
+
+ public void verifySignature(ECPublicKey signatureKey)
+ throws InvalidMessageException
+ {
+ try {
+ byte[][] parts = ByteUtil.split(serialized, serialized.length - SIGNATURE_LENGTH, SIGNATURE_LENGTH);
+
+ if (!Curve.verifySignature(signatureKey, parts[0], parts[1])) {
+ throw new InvalidMessageException("Invalid signature!");
+ }
+
+ } catch (InvalidKeyException e) {
+ throw new InvalidMessageException(e);
+ }
+ }
+
+ private byte[] getSignature(ECPrivateKey signatureKey, byte[] serialized) {
+ try {
+ return Curve.calculateSignature(signatureKey, serialized);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ @Override
+ public byte[] serialize() {
+ return serialized;
+ }
+
+ @Override
+ public int getType() {
+ return CiphertextMessage.SENDERKEY_TYPE;
+ }
+}