diff --git a/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java b/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java index d1d6bb60..687fd122 100644 --- a/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java +++ b/java/src/main/java/org/whispersystems/libsignal/groups/GroupCipher.java @@ -7,10 +7,12 @@ import org.whispersystems.libsignal.DecryptionCallback; import org.whispersystems.libsignal.DuplicateMessageException; +import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyIdException; import org.whispersystems.libsignal.InvalidMessageException; import org.whispersystems.libsignal.LegacyMessageException; import org.whispersystems.libsignal.NoSessionException; +import org.whispersystems.libsignal.ecc.ECPrivateKey; import org.whispersystems.libsignal.groups.ratchet.SenderChainKey; import org.whispersystems.libsignal.groups.ratchet.SenderMessageKey; import org.whispersystems.libsignal.groups.state.SenderKeyRecord; @@ -56,19 +58,25 @@ public GroupCipher(SenderKeyStore senderKeyStore, SenderKeyName senderKeyId) { * @param paddedPlaintext The plaintext message bytes, optionally padded. * @return Ciphertext. * @throws NoSessionException + * @throws InvalidKeyException */ - public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException { + public byte[] encrypt(byte[] paddedPlaintext) throws NoSessionException, InvalidKeyException { synchronized (LOCK) { try { SenderKeyRecord record = senderKeyStore.loadSenderKey(senderKeyId); SenderKeyState senderKeyState = record.getSenderKeyState(); SenderMessageKey senderKey = senderKeyState.getSenderChainKey().getSenderMessageKey(); + ECPrivateKey signatureKey = senderKeyState.getSigningKeyPrivate(); byte[] ciphertext = getCipherText(senderKey.getIv(), senderKey.getCipherKey(), paddedPlaintext); + if (signatureKey == null) { + throw new InvalidKeyException("Session missing signature key!"); + } + SenderKeyMessage senderKeyMessage = new SenderKeyMessage(senderKeyState.getKeyId(), senderKey.getIteration(), ciphertext, - senderKeyState.getSigningKeyPrivate()); + signatureKey); senderKeyState.setSenderChainKey(senderKeyState.getSenderChainKey().getNext()); diff --git a/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java b/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java index 5824d8b4..30b980ae 100644 --- a/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java +++ b/java/src/main/java/org/whispersystems/libsignal/groups/state/SenderKeyState.java @@ -98,8 +98,13 @@ public ECPublicKey getSigningKeyPublic() throws InvalidKeyException { } public ECPrivateKey getSigningKeyPrivate() { - return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() - .getPrivate().toByteArray()); + if (senderKeyStateStructure.hasSenderSigningKey() && + senderKeyStateStructure.getSenderSigningKey().hasPrivate()) { + return Curve.decodePrivatePoint(senderKeyStateStructure.getSenderSigningKey() + .getPrivate().toByteArray()); + } else { + return null; + } } public boolean hasSenderMessageKey(int iteration) { diff --git a/tests/src/test/java/org/whispersystems/libsignal/groups/GroupCipherTest.java b/tests/src/test/java/org/whispersystems/libsignal/groups/GroupCipherTest.java index 41082cd4..f96443ab 100644 --- a/tests/src/test/java/org/whispersystems/libsignal/groups/GroupCipherTest.java +++ b/tests/src/test/java/org/whispersystems/libsignal/groups/GroupCipherTest.java @@ -2,6 +2,7 @@ import junit.framework.TestCase; +import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.SignalProtocolAddress; import org.whispersystems.libsignal.DuplicateMessageException; import org.whispersystems.libsignal.InvalidMessageException; @@ -22,7 +23,7 @@ public class GroupCipherTest extends TestCase { private static final SignalProtocolAddress SENDER_ADDRESS = new SignalProtocolAddress("+14150001111", 1); private static final SenderKeyName GROUP_SENDER = new SenderKeyName("nihilist history reading group", SENDER_ADDRESS); - public void testNoSession() throws InvalidMessageException, LegacyMessageException, NoSessionException, DuplicateMessageException { + public void testNoSession() throws InvalidMessageException, LegacyMessageException, NoSessionException, DuplicateMessageException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -47,7 +48,7 @@ public void testNoSession() throws InvalidMessageException, LegacyMessageExcepti } public void testBasicEncryptDecrypt() - throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException + throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -68,7 +69,7 @@ public void testBasicEncryptDecrypt() assertTrue(new String(plaintextFromAlice).equals("smert ze smert")); } - public void testLargeMessages() throws InvalidMessageException, LegacyMessageException, NoSessionException, DuplicateMessageException { + public void testLargeMessages() throws InvalidMessageException, LegacyMessageException, NoSessionException, DuplicateMessageException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -92,7 +93,7 @@ public void testLargeMessages() throws InvalidMessageException, LegacyMessageExc } public void testBasicRatchet() - throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException + throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -133,7 +134,7 @@ public void testBasicRatchet() assertTrue(new String(plaintextFromAlice3).equals("smert ze smert3")); } - public void testLateJoin() throws NoSessionException, InvalidMessageException, LegacyMessageException, DuplicateMessageException { + public void testLateJoin() throws NoSessionException, InvalidMessageException, LegacyMessageException, DuplicateMessageException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -168,7 +169,7 @@ public void testLateJoin() throws NoSessionException, InvalidMessageException, L public void testOutOfOrder() - throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException + throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -201,7 +202,7 @@ public void testOutOfOrder() } } - public void testEncryptNoSession() { + public void testEncryptNoSession() throws InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); GroupCipher aliceGroupCipher = new GroupCipher(aliceStore, new SenderKeyName("coolio groupio", new SignalProtocolAddress("+10002223333", 1))); try { @@ -213,7 +214,7 @@ public void testEncryptNoSession() { } - public void testTooFarInFuture() throws DuplicateMessageException, InvalidMessageException, LegacyMessageException, NoSessionException { + public void testTooFarInFuture() throws DuplicateMessageException, InvalidMessageException, LegacyMessageException, NoSessionException, InvalidKeyException { InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); @@ -275,6 +276,27 @@ public void testMessageKeyLimit() throws Exception { } } + public void testInvalidSignatureKey() + throws LegacyMessageException, DuplicateMessageException, InvalidMessageException, NoSessionException + { + InMemorySenderKeyStore aliceStore = new InMemorySenderKeyStore(); + InMemorySenderKeyStore bobStore = new InMemorySenderKeyStore(); + + GroupSessionBuilder aliceSessionBuilder = new GroupSessionBuilder(aliceStore); + GroupSessionBuilder bobSessionBuilder = new GroupSessionBuilder(bobStore); + + GroupCipher bobGroupCipher = new GroupCipher(bobStore, GROUP_SENDER); + + SenderKeyDistributionMessage sentAliceDistributionMessage = aliceSessionBuilder.create(GROUP_SENDER); + SenderKeyDistributionMessage receivedAliceDistributionMessage = new SenderKeyDistributionMessage(sentAliceDistributionMessage.serialize()); + bobSessionBuilder.process(GROUP_SENDER, receivedAliceDistributionMessage); + + try { + bobGroupCipher.encrypt("smert ze smert".getBytes()); + } catch (InvalidKeyException e) { + // good + } + } private int randomInt() { try {