aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java52
-rw-r--r--tests/src/test/java/org/whispersystems/libaxolotl/groups/GroupCipherTest.java46
2 files changed, 87 insertions, 11 deletions
diff --git a/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java b/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java
index 424dd87c..35f9e218 100644
--- a/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java
+++ b/java/src/main/java/org/whispersystems/libaxolotl/protocol/SenderKeyDistributionMessage.java
@@ -1,7 +1,12 @@
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.ECPublicKey;
import org.whispersystems.libaxolotl.util.ByteUtil;
@@ -15,17 +20,52 @@ public class SenderKeyDistributionMessage implements CiphertextMessage {
public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) {
byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)};
+ byte[] protobuf = WhisperProtos.SenderKeyDistributionMessage.newBuilder()
+ .setId(id)
+ .setIteration(iteration)
+ .setChainKey(ByteString.copyFrom(chainKey))
+ .setSigningKey(ByteString.copyFrom(signatureKey.serialize()))
+ .build().toByteArray();
this.id = id;
this.iteration = iteration;
this.chainKey = chainKey;
this.signatureKey = signatureKey;
- this.serialized = WhisperProtos.SenderKeyDistributionMessage.newBuilder()
- .setId(id)
- .setIteration(iteration)
- .setChainKey(ByteString.copyFrom(chainKey))
- .setSigningKey(ByteString.copyFrom(signatureKey.serialize()))
- .build().toByteArray();
+ this.serialized = ByteUtil.combine(version, protobuf);
+ }
+
+ public SenderKeyDistributionMessage(byte[] serialized) throws LegacyMessageException, InvalidMessageException {
+ try {
+ byte[][] messageParts = ByteUtil.split(serialized, 1, serialized.length - 1);
+ byte version = messageParts[0][0];
+ byte[] message = messageParts[1];
+
+ if (ByteUtil.highBitsToInt(version) < CiphertextMessage.CURRENT_VERSION) {
+ throw new LegacyMessageException("Legacy message: " + ByteUtil.highBitsToInt(version));
+ }
+
+ if (ByteUtil.highBitsToInt(version) > CURRENT_VERSION) {
+ throw new InvalidMessageException("Unknown version: " + ByteUtil.highBitsToInt(version));
+ }
+
+ WhisperProtos.SenderKeyDistributionMessage distributionMessage = WhisperProtos.SenderKeyDistributionMessage.parseFrom(message);
+
+ if (!distributionMessage.hasId() ||
+ !distributionMessage.hasIteration() ||
+ !distributionMessage.hasChainKey() ||
+ !distributionMessage.hasSigningKey())
+ {
+ throw new InvalidMessageException("Incomplete message.");
+ }
+
+ this.serialized = serialized;
+ this.id = distributionMessage.getId();
+ this.iteration = distributionMessage.getIteration();
+ this.chainKey = distributionMessage.getChainKey().toByteArray();
+ this.signatureKey = Curve.decodePoint(distributionMessage.getSigningKey().toByteArray(), 0);
+ } catch (InvalidProtocolBufferException | InvalidKeyException e) {
+ throw new InvalidMessageException(e);
+ }
}
@Override
diff --git a/tests/src/test/java/org/whispersystems/libaxolotl/groups/GroupCipherTest.java b/tests/src/test/java/org/whispersystems/libaxolotl/groups/GroupCipherTest.java
index dd5a306f..32fcc652 100644
--- a/tests/src/test/java/org/whispersystems/libaxolotl/groups/GroupCipherTest.java
+++ b/tests/src/test/java/org/whispersystems/libaxolotl/groups/GroupCipherTest.java
@@ -30,9 +30,9 @@ public class GroupCipherTest extends TestCase {
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, GROUP_SENDER);
GroupCipher bobGroupCipher = new GroupCipher(bobStore, GROUP_SENDER);
- SenderKeyDistributionMessage aliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER);
-
- bobSessionBuilder.process(GROUP_SENDER, aliceDistributionMessage);
+ SenderKeyDistributionMessage sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER);
+ SenderKeyDistributionMessage receivedAliceDistributionMessage = new SenderKeyDistributionMessage(sentAliceDistributionMessage.serialize());
+ bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage);
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
byte[] plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice);
@@ -54,10 +54,12 @@ public class GroupCipherTest extends TestCase {
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName);
- SenderKeyDistributionMessage aliceDistributionMessage =
+ SenderKeyDistributionMessage sentAliceDistributionMessage =
aliceSessionBuilder.create(aliceName);
+ SenderKeyDistributionMessage receivedAliceDistributionMessage =
+ new SenderKeyDistributionMessage(sentAliceDistributionMessage.serialize());
- bobSessionBuilder.process(aliceName, aliceDistributionMessage);
+ bobSessionBuilder.process(aliceName, receivedAliceDistributionMessage);
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
byte[] ciphertextFromAlice2 = aliceGroupCipher.encrypt("smert ze smert2".getBytes());
@@ -80,6 +82,40 @@ public class GroupCipherTest extends TestCase {
assertTrue(new String(plaintextFromAlice3).equals("smert ze smert3"));
}
+ public void testLateJoin() throws NoSessionException, InvalidMessageException, LegacyMessageException, DuplicateMessageException {
+ InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore();
+ InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore();
+
+ GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore);
+
+
+ SenderKeyName aliceName = GROUP_SENDER;
+
+ GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
+
+
+ SenderKeyDistributionMessage aliceDistributionMessage = aliceSessionBuilder.create(aliceName);
+ // Send off to some people.
+
+ for (int i=0;i<100;i++) {
+ aliceGroupCipher.encrypt("up the punks up the punks up the punks".getBytes());
+ }
+
+ // Now Bob Joins.
+ GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore);
+ GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName);
+
+
+ SenderKeyDistributionMessage distributionMessageToBob = aliceSessionBuilder.create(aliceName);
+ bobSessionBuilder.process(aliceName, new SenderKeyDistributionMessage(distributionMessageToBob.serialize()));
+
+ byte[] ciphertext = aliceGroupCipher.encrypt("welcome to the group".getBytes());
+ byte[] plaintext = bobGroupCipher.decrypt(ciphertext);
+
+ assertEquals(new String(plaintext), "welcome to the group");
+ }
+
+
public void testOutOfOrder()
throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException
{