Make it possible to deserialize SenderKeyDistributionMessages

This commit is contained in:
Moxie Marlinspike 2015-03-17 10:57:31 -07:00
parent aa1c41c980
commit bf9c8708e0
2 changed files with 87 additions and 11 deletions

View File

@ -1,7 +1,12 @@
package org.whispersystems.libaxolotl.protocol; package org.whispersystems.libaxolotl.protocol;
import com.google.protobuf.ByteString; 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.ecc.ECPublicKey;
import org.whispersystems.libaxolotl.util.ByteUtil; 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) { public SenderKeyDistributionMessage(int id, int iteration, byte[] chainKey, ECPublicKey signatureKey) {
byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)}; byte[] version = {ByteUtil.intsToByteHighAndLow(CURRENT_VERSION, CURRENT_VERSION)};
byte[] protobuf = WhisperProtos.SenderKeyDistributionMessage.newBuilder()
this.id = id;
this.iteration = iteration;
this.chainKey = chainKey;
this.signatureKey = signatureKey;
this.serialized = WhisperProtos.SenderKeyDistributionMessage.newBuilder()
.setId(id) .setId(id)
.setIteration(iteration) .setIteration(iteration)
.setChainKey(ByteString.copyFrom(chainKey)) .setChainKey(ByteString.copyFrom(chainKey))
.setSigningKey(ByteString.copyFrom(signatureKey.serialize())) .setSigningKey(ByteString.copyFrom(signatureKey.serialize()))
.build().toByteArray(); .build().toByteArray();
this.id = id;
this.iteration = iteration;
this.chainKey = chainKey;
this.signatureKey = signatureKey;
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 @Override

View File

@ -30,9 +30,9 @@ public class GroupCipherTest extends TestCase {
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, GROUP_SENDER); GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, GROUP_SENDER);
GroupCipher bobGroupCipher = new GroupCipher(bobStore, GROUP_SENDER); GroupCipher bobGroupCipher = new GroupCipher(bobStore, GROUP_SENDER);
SenderKeyDistributionMessage aliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); SenderKeyDistributionMessage sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER);
SenderKeyDistributionMessage receivedAliceDistributionMessage = new SenderKeyDistributionMessage(sentAliceDistributionMessage.serialize());
bobSessionBuilder.process(GROUP_SENDER, aliceDistributionMessage); bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage);
byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes()); byte[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
byte[] plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice); byte[] plaintextFromAlice = bobGroupCipher.decrypt(ciphertextFromAlice);
@ -54,10 +54,12 @@ public class GroupCipherTest extends TestCase {
GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName); GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, aliceName);
GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName); GroupCipher bobGroupCipher = new GroupCipher(bobStore, aliceName);
SenderKeyDistributionMessage aliceDistributionMessage = SenderKeyDistributionMessage sentAliceDistributionMessage =
aliceSessionBuilder.create(aliceName); 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[] ciphertextFromAlice = aliceGroupCipher.encrypt("smert ze smert".getBytes());
byte[] ciphertextFromAlice2 = aliceGroupCipher.encrypt("smert ze smert2".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")); 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() public void testOutOfOrder()
throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException
{ {