170 lines
6.7 KiB
Java
170 lines
6.7 KiB
Java
/**
|
|
*
|
|
* Copyright 2017 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 static org.junit.Assert.fail;
|
|
|
|
import java.util.concurrent.TimeoutException;
|
|
|
|
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 {
|
|
|
|
public SessionRenegotiationIntegrationTest(SmackIntegrationTestEnvironment environment)
|
|
throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
|
SmackException.NoResponseException, TestNotPossibleException {
|
|
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.
|
|
*/
|
|
OmemoMessage.Sent e1 = alice.encrypt(bob.getOwnJid(), body1);
|
|
Message m1 = new Message();
|
|
m1.addExtension(e1.getElement());
|
|
m1.setTo(bob.getOwnJid());
|
|
|
|
bob.addOmemoMessageListener(bml1);
|
|
alice.getConnection().sendStanza(m1);
|
|
bsp1.waitForResult(10 * 1000);
|
|
bob.removeOmemoMessageListener(bml1);
|
|
|
|
/*
|
|
Delete the session records on Bobs side to render the session invalid.
|
|
*/
|
|
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.
|
|
*/
|
|
OmemoMessage.Sent e3 = alice.encrypt(bob.getOwnJid(), body3);
|
|
Message m3 = new Message();
|
|
m3.addExtension(e3.getElement());
|
|
m3.setTo(bob.getOwnJid());
|
|
|
|
bob.addOmemoMessageListener(bml3);
|
|
alice.getConnection().sendStanza(m3);
|
|
bsp3.waitForResult(10 * 1000);
|
|
bob.removeOmemoMessageListener(bml3);
|
|
}
|
|
|
|
private static class OmemoTestMessageListener implements OmemoMessageListener {
|
|
|
|
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) {
|
|
|
|
}
|
|
}
|
|
}
|