1
0
Fork 0
mirror of https://github.com/vanitasvitae/Smack.git synced 2025-12-07 11:31:10 +01:00

Rewrite integration tests

This commit is contained in:
Paul Schaub 2018-01-09 15:18:28 +01:00
parent 8b8c6e190a
commit f3403571a7
11 changed files with 580 additions and 319 deletions

View file

@ -40,5 +40,8 @@ public abstract class AbstractOmemoIntegrationTest extends AbstractSmackIntegrat
if (!OmemoService.isServiceRegistered()) {
throw new TestNotPossibleException("No OmemoService registered.");
}
OmemoConfiguration.setCompleteSessionWithEmptyMessage(true);
OmemoConfiguration.setRepairBrokenSessionsWithPrekeyMessages(true);
}
}

View file

@ -0,0 +1,149 @@
/**
*
* Copyright 2018 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.omemo;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
/**
* Convenience class. This listener is used so that implementers of OmemoMessageListener don't have to implement
* both messages. Instead they can just overwrite the message they want to implement.
*/
public class AbstractOmemoMessageListener implements OmemoMessageListener {
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage) {
// Override me
}
@Override
public void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage, OmemoMessage.Received decryptedCarbonCopy) {
// Override me
}
private static class SyncPointListener extends AbstractOmemoMessageListener {
protected final ResultSyncPoint<?,?> syncPoint;
public SyncPointListener(ResultSyncPoint<?,?> syncPoint) {
this.syncPoint = syncPoint;
}
public ResultSyncPoint<?, ?> getSyncPoint() {
return syncPoint;
}
}
static class MessageListener extends SyncPointListener {
protected final String expectedMessage;
MessageListener(String expectedMessage, SimpleResultSyncPoint syncPoint) {
super(syncPoint);
this.expectedMessage = expectedMessage;
}
MessageListener(String expectedMessage) {
this(expectedMessage, new SimpleResultSyncPoint());
}
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
SimpleResultSyncPoint srp = (SimpleResultSyncPoint) syncPoint;
if (received.isKeyTransportMessage()) {
return;
}
if (received.getBody().equals(expectedMessage)) {
srp.signal();
} else {
srp.signalFailure("Received decrypted message was not equal to sent message.");
}
}
}
static class PreKeyMessageListener extends MessageListener {
PreKeyMessageListener(String expectedMessage, SimpleResultSyncPoint syncPoint) {
super(expectedMessage, syncPoint);
}
PreKeyMessageListener(String expectedMessage) {
this(expectedMessage, new SimpleResultSyncPoint());
}
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
SimpleResultSyncPoint srp = (SimpleResultSyncPoint) syncPoint;
if (received.isKeyTransportMessage()) {
return;
}
if (received.isPreKeyMessage()) {
if (received.getBody().equals(expectedMessage)) {
srp.signal();
} else {
srp.signalFailure("Received decrypted message was not equal to sent message.");
}
} else {
srp.signalFailure("Received message was not a PreKeyMessage.");
}
}
}
static class KeyTransportListener extends SyncPointListener {
KeyTransportListener(SimpleResultSyncPoint resultSyncPoint) {
super(resultSyncPoint);
}
KeyTransportListener() {
this(new SimpleResultSyncPoint());
}
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
SimpleResultSyncPoint s = (SimpleResultSyncPoint) syncPoint;
if (received.isKeyTransportMessage()) {
s.signal();
}
}
}
static class PreKeyKeyTransportListener extends KeyTransportListener {
PreKeyKeyTransportListener(SimpleResultSyncPoint resultSyncPoint) {
super(resultSyncPoint);
}
PreKeyKeyTransportListener() {
this(new SimpleResultSyncPoint());
}
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
SimpleResultSyncPoint s = (SimpleResultSyncPoint) syncPoint;
if (received.isPreKeyMessage()) {
if (received.isKeyTransportMessage()) {
s.signal();
}
}
}
}
}

View file

@ -17,19 +17,15 @@
package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
/**
* Simple OMEMO message encryption integration test.
@ -44,77 +40,71 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
super(environment);
}
/**
* This test checks whether the following actions are performed.
*
* Alice publishes bundle A1
* Bob publishes bundle B1
*
* Alice sends message to Bob (preKeyMessage)
* Bob publishes bundle B2
* Alice still has A1
*
* (Alice sends second message to bob to avoid race condition in the code (just for this example))
*
* Bob responds to Alice (normal message)
* Alice still has A1
* Bob still has B2
* @throws Exception
*/
@SmackIntegrationTest
public void messageTest() throws Exception {
OmemoBundleElement aliceBundle1 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement bobsBundle1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
OmemoBundleElement a1 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement b1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
final String message1 = "One is greater than zero (for small values of zero).";
OmemoMessage.Sent encrypted1 = alice.encrypt(bob.getOwnJid(), message1);
final SimpleResultSyncPoint bobReceivedMessage = new SimpleResultSyncPoint();
// Alice sends message(s) to bob
// PreKeyMessage A -> B
final String body1 = "One is greater than zero (for small values of zero).";
AbstractOmemoMessageListener.PreKeyMessageListener listener1 =
new AbstractOmemoMessageListener.PreKeyMessageListener(body1);
bob.addOmemoMessageListener(listener1);
OmemoMessage.Sent e1 = alice.encrypt(bob.getOwnJid(), body1);
alice.getConnection().sendStanza(e1.asMessage(bob.getOwnJid()));
listener1.getSyncPoint().waitForResult(10 * 1000);
bob.removeOmemoMessageListener(listener1);
bob.addOmemoMessageListener(new OmemoMessageListener() {
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
if (received.getMessage().equals(message1)) {
bobReceivedMessage.signal();
} else {
bobReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
}
}
// Message A -> B
final String body2 = "This message is sent to mitigate a race condition in the test";
AbstractOmemoMessageListener.MessageListener listener2 =
new AbstractOmemoMessageListener.MessageListener(body2);
bob.addOmemoMessageListener(listener2);
OmemoMessage.Sent e2 = alice.encrypt(bob.getOwnJid(), body2);
alice.getConnection().sendStanza(e2.asMessage(bob.getOwnJid()));
listener2.getSyncPoint().waitForResult(10 * 1000);
bob.removeOmemoMessageListener(listener2);
@Override
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
// Not needed
}
OmemoBundleElement a1_ = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement b2 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
});
assertEquals("Alice sent bob a preKeyMessage, so her bundle MUST still be the same.", a1, a1_);
assertNotEquals("Bob just received a preKeyMessage from alice, so his bundle must have changed.", b1, b2);
Message m1 = new Message();
m1.addExtension(encrypted1.getElement());
m1.setTo(bob.getOwnJid());
alice.getConnection().sendStanza(m1);
bobReceivedMessage.waitForResult(10 * 1000);
// Message B -> A
final String body3 = "The german words for 'leek' and 'wimp' are the same.";
AbstractOmemoMessageListener.MessageListener listener3 =
new AbstractOmemoMessageListener.MessageListener(body3);
alice.addOmemoMessageListener(listener3);
OmemoMessage.Sent e3 = bob.encrypt(alice.getOwnJid(), body3);
bob.getConnection().sendStanza(e3.asMessage(alice.getOwnJid()));
listener3.getSyncPoint().waitForResult(10 * 1000);
alice.removeOmemoMessageListener(listener3);
OmemoBundleElement aliceBundle2 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement bobsBundle2 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
OmemoBundleElement a1__ = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement b2_ = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
// Alice bundle is still the same, but bobs bundle changed, because he used up a pre-key.
assertEquals(aliceBundle1, aliceBundle2);
assertFalse(bobsBundle1.equals(bobsBundle2));
final String message2 = "The german words for 'leek' and 'wimp' are the same.";
final OmemoMessage.Sent encrypted2 = bob.encrypt(alice.getOwnJid(), message2);
final SimpleResultSyncPoint aliceReceivedMessage = new SimpleResultSyncPoint();
alice.addOmemoMessageListener(new OmemoMessageListener() {
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
if (received.getMessage().equals(message2)) {
aliceReceivedMessage.signal();
} else {
aliceReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
}
}
@Override
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
// Not needed
}
});
Message m2 = new Message();
m2.addExtension(encrypted2.getElement());
m2.setTo(alice.getOwnJid());
bob.getConnection().sendStanza(m2);
aliceReceivedMessage.waitForResult(10 * 1000);
OmemoBundleElement aliceBundle3 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
OmemoBundleElement bobsBundle3 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
// Alice bundle did not change, because she already has a session with bob, which he initiated.
// Bobs bundle doesn't change this time.
assertEquals(aliceBundle2, aliceBundle3);
assertEquals(bobsBundle2, bobsBundle3);
assertEquals("Since alice initiated the session with bob, at no time he sent a preKeyMessage, " +
"so her bundle MUST still be the same.", a1_, a1__);
assertEquals("Bob changed his bundle earlier, but at this point his bundle must be equal to " +
"after the first change.", b2, b2_);
}
}

View file

@ -16,21 +16,14 @@
*/
package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.fail;
import java.util.concurrent.TimeoutException;
import java.util.logging.Level;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.Stanza;
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
import org.igniterealtime.smack.inttest.TestNotPossibleException;
import org.igniterealtime.smack.inttest.util.ResultSyncPoint;
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest {
@ -40,130 +33,66 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
super(environment);
}
private static final String body1 = "P = NP is true for all N,P from the set of complex numbers, where P is equal to 0";
private final SimpleResultSyncPoint bsp1 = new SimpleResultSyncPoint();
private final OmemoMessageListener bml1 = new OmemoTestMessageListener(body1, bsp1);
private static final String body2 = "P = NP is also true for all N,P from the set of complex numbers, where N is equal to 1.";
private final ResultSyncPoint<Boolean, IllegalStateException> bsp2 = new ResultSyncPoint<>();
private final OmemoMessageListener bml2 = new OmemoMessageListener() {
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
if (received.getMessage().equals(body2)) {
bsp2.signal(new IllegalStateException("Message MUST NOT be decryptable!"));
} else {
bsp2.signal(new IllegalStateException("OmemoMessageListener MUST NOT be called for this message."));
}
}
@Override
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
// Not needed
}
};
private final SimpleResultSyncPoint asp2 = new SimpleResultSyncPoint();
private final OmemoMessageListener aml2 = new OmemoMessageListener() {
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage) {
// Not needed
}
@Override
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received received) {
asp2.signal();
}
};
private static final String body3 = "P = NP would be a disaster for the world of cryptography.";
private final SimpleResultSyncPoint bsp3 = new SimpleResultSyncPoint();
private final OmemoMessageListener bml3 = new OmemoTestMessageListener(body3, bsp3);
@SmackIntegrationTest
public void sessionRenegotiationTest() throws Exception {
/*
Send (PreKey-)message from Alice to Bob to initiate a session.
*/
if (!OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages()) {
throw new TestNotPossibleException("This test requires the property " +
"OmemoConfiguration.REPAIR_BROKEN_SESSIONS_WITH_PREKEY_MESSAGES " +
"set to 'true'.");
}
final String body1 = "P = NP is true for all N,P from the set of complex numbers, where P is equal to 0";
AbstractOmemoMessageListener.PreKeyMessageListener listener1 =
new AbstractOmemoMessageListener.PreKeyMessageListener(body1);
OmemoMessage.Sent e1 = alice.encrypt(bob.getOwnJid(), body1);
Message m1 = new Message();
m1.addExtension(e1.getElement());
m1.setTo(bob.getOwnJid());
bob.addOmemoMessageListener(listener1);
alice.getConnection().sendStanza(e1.asMessage(bob.getOwnJid()));
LOGGER.log(Level.INFO, "Message 1 sent");
listener1.getSyncPoint().waitForResult(10 * 1000);
bob.removeOmemoMessageListener(listener1);
bob.addOmemoMessageListener(bml1);
alice.getConnection().sendStanza(m1);
bsp1.waitForResult(10 * 1000);
bob.removeOmemoMessageListener(bml1);
LOGGER.log(Level.INFO, "Message 1 received");
/*
Delete the session records on Bobs side to render the session invalid.
*/
final String body2 = "This message is sent to circumvent a race condition in the test.";
AbstractOmemoMessageListener.MessageListener listener2 = new AbstractOmemoMessageListener.MessageListener(body2);
OmemoMessage.Sent e2 = alice.encrypt(bob.getOwnJid(), body2);
bob.addOmemoMessageListener(listener2);
alice.getConnection().sendStanza(e2.asMessage(bob.getOwnJid()));
LOGGER.log(Level.INFO, "Message 2 sent");
listener2.getSyncPoint().waitForResult(10 * 1000);
bob.removeOmemoMessageListener(listener2);
LOGGER.log(Level.INFO, "Message 2 received");
// Remove bobs session with alice.
LOGGER.log(Level.INFO, "Delete session");
bob.getOmemoService().getOmemoStoreBackend().removeRawSession(bob.getOwnDevice(), alice.getOwnDevice());
/*
Send normal message from Alice to Bob (Alice assumes, that Bob still has a valid session).
*/
OmemoMessage.Sent e2 = alice.encrypt(bob.getOwnJid(), body2);
Message m2 = new Message();
m2.addExtension(e2.getElement());
m2.setTo(bob.getOwnJid());
bob.addOmemoMessageListener(bml2);
alice.addOmemoMessageListener(aml2);
alice.getConnection().sendStanza(m2);
/*
Wait for the timeout on Bobs side, since message decryption will fail now.
Bob will respond with an empty PreKeyMessage though, in order to repair the session.
*/
try {
bsp2.waitForResult(10 * 1000);
fail("This MUST throw a TimeoutException.");
} catch (IllegalStateException e) {
fail(e.getMessage());
} catch (TimeoutException e) {
// Expected.
}
asp2.waitForResult(10 * 1000);
bob.removeOmemoMessageListener(bml2);
alice.removeOmemoMessageListener(aml2);
/*
Since Bob responded with a PreKeyMessage to repair the broken session, Alice should now be able to send messages
which Bob can decrypt successfully again.
*/
final String body3 = "P = NP is also true for all N,P from the set of complex numbers, where N is equal to 1.";
AbstractOmemoMessageListener.PreKeyKeyTransportListener listener3 =
new AbstractOmemoMessageListener.PreKeyKeyTransportListener();
OmemoMessage.Sent e3 = alice.encrypt(bob.getOwnJid(), body3);
Message m3 = new Message();
m3.addExtension(e3.getElement());
m3.setTo(bob.getOwnJid());
alice.addOmemoMessageListener(listener3);
alice.getConnection().sendStanza(e3.asMessage(bob.getOwnJid()));
LOGGER.log(Level.INFO, "Message 3 sent");
listener3.getSyncPoint().waitForResult(10 * 1000);
alice.removeOmemoMessageListener(listener3);
bob.addOmemoMessageListener(bml3);
alice.getConnection().sendStanza(m3);
bsp3.waitForResult(10 * 1000);
bob.removeOmemoMessageListener(bml3);
}
LOGGER.log(Level.INFO, "Message 3 received");
private static class OmemoTestMessageListener implements OmemoMessageListener {
final String body4 = "P = NP would be a disaster for the world of cryptography.";
AbstractOmemoMessageListener.MessageListener listener4 = new AbstractOmemoMessageListener.MessageListener(body4);
LOGGER.log(Level.INFO, "Attempt to encrypt message 4");
OmemoMessage.Sent e4 = alice.encrypt(bob.getOwnJid(), body4);
LOGGER.log(Level.INFO, "Message 4 encrypted");
bob.addOmemoMessageListener(listener4);
alice.getConnection().sendStanza(e4.asMessage(bob.getOwnJid()));
LOGGER.log(Level.INFO, "Message 4 sent");
listener4.getSyncPoint().waitForResult(10 * 1000);
bob.removeOmemoMessageListener(listener4);
private final String expectedMessage;
private final SimpleResultSyncPoint syncPoint;
OmemoTestMessageListener(String expectedMessage, SimpleResultSyncPoint syncPoint) {
this.expectedMessage = expectedMessage;
this.syncPoint = syncPoint;
}
@Override
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
if (received.getMessage().equals(expectedMessage)) {
syncPoint.signal();
} else {
syncPoint.signalFailure("Received decrypted message was not equal to sent message.");
}
}
@Override
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
}
LOGGER.log(Level.INFO, "Message 4 received");
}
}