mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-27 14:32:06 +01:00
Rewrite integration tests
This commit is contained in:
parent
8b8c6e190a
commit
f3403571a7
11 changed files with 580 additions and 319 deletions
|
@ -40,5 +40,8 @@ public abstract class AbstractOmemoIntegrationTest extends AbstractSmackIntegrat
|
||||||
if (!OmemoService.isServiceRegistered()) {
|
if (!OmemoService.isServiceRegistered()) {
|
||||||
throw new TestNotPossibleException("No OmemoService registered.");
|
throw new TestNotPossibleException("No OmemoService registered.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OmemoConfiguration.setCompleteSessionWithEmptyMessage(true);
|
||||||
|
OmemoConfiguration.setRepairBrokenSessionsWithPrekeyMessages(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,19 +17,15 @@
|
||||||
package org.jivesoftware.smackx.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
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.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
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.element.OmemoBundleElement;
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
||||||
import org.igniterealtime.smack.inttest.util.SimpleResultSyncPoint;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Simple OMEMO message encryption integration test.
|
* Simple OMEMO message encryption integration test.
|
||||||
|
@ -44,77 +40,71 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
|
||||||
super(environment);
|
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
|
@SmackIntegrationTest
|
||||||
public void messageTest() throws Exception {
|
public void messageTest() throws Exception {
|
||||||
OmemoBundleElement aliceBundle1 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
OmemoBundleElement a1 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
||||||
OmemoBundleElement bobsBundle1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
OmemoBundleElement b1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
||||||
|
|
||||||
final String message1 = "One is greater than zero (for small values of zero).";
|
// Alice sends message(s) to bob
|
||||||
OmemoMessage.Sent encrypted1 = alice.encrypt(bob.getOwnJid(), message1);
|
// PreKeyMessage A -> B
|
||||||
final SimpleResultSyncPoint bobReceivedMessage = new SimpleResultSyncPoint();
|
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() {
|
// Message A -> B
|
||||||
@Override
|
final String body2 = "This message is sent to mitigate a race condition in the test";
|
||||||
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
|
AbstractOmemoMessageListener.MessageListener listener2 =
|
||||||
if (received.getMessage().equals(message1)) {
|
new AbstractOmemoMessageListener.MessageListener(body2);
|
||||||
bobReceivedMessage.signal();
|
bob.addOmemoMessageListener(listener2);
|
||||||
} else {
|
OmemoMessage.Sent e2 = alice.encrypt(bob.getOwnJid(), body2);
|
||||||
bobReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
|
alice.getConnection().sendStanza(e2.asMessage(bob.getOwnJid()));
|
||||||
}
|
listener2.getSyncPoint().waitForResult(10 * 1000);
|
||||||
}
|
bob.removeOmemoMessageListener(listener2);
|
||||||
|
|
||||||
@Override
|
OmemoBundleElement a1_ = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
||||||
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
|
OmemoBundleElement b2 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
||||||
// Not needed
|
|
||||||
}
|
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 B -> A
|
||||||
Message m1 = new Message();
|
final String body3 = "The german words for 'leek' and 'wimp' are the same.";
|
||||||
m1.addExtension(encrypted1.getElement());
|
AbstractOmemoMessageListener.MessageListener listener3 =
|
||||||
m1.setTo(bob.getOwnJid());
|
new AbstractOmemoMessageListener.MessageListener(body3);
|
||||||
alice.getConnection().sendStanza(m1);
|
alice.addOmemoMessageListener(listener3);
|
||||||
bobReceivedMessage.waitForResult(10 * 1000);
|
OmemoMessage.Sent e3 = bob.encrypt(alice.getOwnJid(), body3);
|
||||||
|
bob.getConnection().sendStanza(e3.asMessage(alice.getOwnJid()));
|
||||||
OmemoBundleElement aliceBundle2 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
listener3.getSyncPoint().waitForResult(10 * 1000);
|
||||||
OmemoBundleElement bobsBundle2 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
alice.removeOmemoMessageListener(listener3);
|
||||||
|
|
||||||
// Alice bundle is still the same, but bobs bundle changed, because he used up a pre-key.
|
OmemoBundleElement a1__ = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
||||||
assertEquals(aliceBundle1, aliceBundle2);
|
OmemoBundleElement b2_ = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
||||||
assertFalse(bobsBundle1.equals(bobsBundle2));
|
|
||||||
|
assertEquals("Since alice initiated the session with bob, at no time he sent a preKeyMessage, " +
|
||||||
final String message2 = "The german words for 'leek' and 'wimp' are the same.";
|
"so her bundle MUST still be the same.", a1_, a1__);
|
||||||
final OmemoMessage.Sent encrypted2 = bob.encrypt(alice.getOwnJid(), message2);
|
assertEquals("Bob changed his bundle earlier, but at this point his bundle must be equal to " +
|
||||||
final SimpleResultSyncPoint aliceReceivedMessage = new SimpleResultSyncPoint();
|
"after the first change.", b2, b2_);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,21 +16,14 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.fail;
|
import java.util.logging.Level;
|
||||||
|
|
||||||
import java.util.concurrent.TimeoutException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
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.SmackIntegrationTest;
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment;
|
||||||
import org.igniterealtime.smack.inttest.TestNotPossibleException;
|
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 class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest {
|
||||||
|
|
||||||
|
@ -40,130 +33,66 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
super(environment);
|
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
|
@SmackIntegrationTest
|
||||||
public void sessionRenegotiationTest() throws Exception {
|
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);
|
OmemoMessage.Sent e1 = alice.encrypt(bob.getOwnJid(), body1);
|
||||||
Message m1 = new Message();
|
bob.addOmemoMessageListener(listener1);
|
||||||
m1.addExtension(e1.getElement());
|
alice.getConnection().sendStanza(e1.asMessage(bob.getOwnJid()));
|
||||||
m1.setTo(bob.getOwnJid());
|
LOGGER.log(Level.INFO, "Message 1 sent");
|
||||||
|
listener1.getSyncPoint().waitForResult(10 * 1000);
|
||||||
|
bob.removeOmemoMessageListener(listener1);
|
||||||
|
|
||||||
bob.addOmemoMessageListener(bml1);
|
LOGGER.log(Level.INFO, "Message 1 received");
|
||||||
alice.getConnection().sendStanza(m1);
|
|
||||||
bsp1.waitForResult(10 * 1000);
|
|
||||||
bob.removeOmemoMessageListener(bml1);
|
|
||||||
|
|
||||||
/*
|
final String body2 = "This message is sent to circumvent a race condition in the test.";
|
||||||
Delete the session records on Bobs side to render the session invalid.
|
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());
|
bob.getOmemoService().getOmemoStoreBackend().removeRawSession(bob.getOwnDevice(), alice.getOwnDevice());
|
||||||
|
|
||||||
/*
|
final String body3 = "P = NP is also true for all N,P from the set of complex numbers, where N is equal to 1.";
|
||||||
Send normal message from Alice to Bob (Alice assumes, that Bob still has a valid session).
|
AbstractOmemoMessageListener.PreKeyKeyTransportListener listener3 =
|
||||||
*/
|
new AbstractOmemoMessageListener.PreKeyKeyTransportListener();
|
||||||
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);
|
OmemoMessage.Sent e3 = alice.encrypt(bob.getOwnJid(), body3);
|
||||||
Message m3 = new Message();
|
alice.addOmemoMessageListener(listener3);
|
||||||
m3.addExtension(e3.getElement());
|
alice.getConnection().sendStanza(e3.asMessage(bob.getOwnJid()));
|
||||||
m3.setTo(bob.getOwnJid());
|
LOGGER.log(Level.INFO, "Message 3 sent");
|
||||||
|
listener3.getSyncPoint().waitForResult(10 * 1000);
|
||||||
|
alice.removeOmemoMessageListener(listener3);
|
||||||
|
|
||||||
bob.addOmemoMessageListener(bml3);
|
LOGGER.log(Level.INFO, "Message 3 received");
|
||||||
alice.getConnection().sendStanza(m3);
|
|
||||||
bsp3.waitForResult(10 * 1000);
|
|
||||||
bob.removeOmemoMessageListener(bml3);
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
LOGGER.log(Level.INFO, "Message 4 received");
|
||||||
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) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -93,7 +93,7 @@ public final class OmemoManager extends Manager {
|
||||||
private static final Logger LOGGER = Logger.getLogger(OmemoManager.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(OmemoManager.class.getName());
|
||||||
|
|
||||||
private static final Integer UNKNOWN_DEVICE_ID = -1;
|
private static final Integer UNKNOWN_DEVICE_ID = -1;
|
||||||
private final Object LOCK = new Object();
|
final Object LOCK = new Object();
|
||||||
|
|
||||||
private static final WeakHashMap<XMPPConnection, TreeMap<Integer,OmemoManager>> INSTANCES = new WeakHashMap<>();
|
private static final WeakHashMap<XMPPConnection, TreeMap<Integer,OmemoManager>> INSTANCES = new WeakHashMap<>();
|
||||||
private final OmemoService<?, ?, ?, ?, ?, ?, ?, ?, ?> service;
|
private final OmemoService<?, ?, ?, ?, ?, ?, ?, ?, ?> service;
|
||||||
|
@ -613,18 +613,37 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an OmemoMessageListener. This listener will be informed about incoming OMEMO messages
|
||||||
|
* (as well as KeyTransportMessages) and OMEMO encrypted message carbons.
|
||||||
|
*
|
||||||
|
* @param listener OmemoMessageListener
|
||||||
|
*/
|
||||||
public void addOmemoMessageListener(OmemoMessageListener listener) {
|
public void addOmemoMessageListener(OmemoMessageListener listener) {
|
||||||
omemoMessageListeners.add(listener);
|
omemoMessageListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an OmemoMessageListener.
|
||||||
|
* @param listener OmemoMessageListener
|
||||||
|
*/
|
||||||
public void removeOmemoMessageListener(OmemoMessageListener listener) {
|
public void removeOmemoMessageListener(OmemoMessageListener listener) {
|
||||||
omemoMessageListeners.remove(listener);
|
omemoMessageListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add an OmemoMucMessageListener. This listener will be informed about incoming OMEMO encrypted MUC messages.
|
||||||
|
*
|
||||||
|
* @param listener OmemoMessageListener.
|
||||||
|
*/
|
||||||
public void addOmemoMucMessageListener(OmemoMucMessageListener listener) {
|
public void addOmemoMucMessageListener(OmemoMucMessageListener listener) {
|
||||||
omemoMucMessageListeners.add(listener);
|
omemoMucMessageListeners.add(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove an OmemoMucMessageListener.
|
||||||
|
* @param listener OmemoMucMessageListener
|
||||||
|
*/
|
||||||
public void removeOmemoMucMessageListener(OmemoMucMessageListener listener) {
|
public void removeOmemoMucMessageListener(OmemoMucMessageListener listener) {
|
||||||
omemoMucMessageListeners.remove(listener);
|
omemoMucMessageListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
@ -787,25 +806,12 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notify all registered OmemoMessageListeners about a received OMEMO KeyTransportMessage.
|
|
||||||
*
|
|
||||||
* @param stanza original stanza.
|
|
||||||
* @param decryptedKeyTransportMessage decrypted KeyTransportMessage.
|
|
||||||
*/
|
|
||||||
void notifyOmemoKeyTransportMessageReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage)
|
|
||||||
{
|
|
||||||
for (OmemoMessageListener l : omemoMessageListeners) {
|
|
||||||
l.onOmemoKeyTransportReceived(stanza, decryptedKeyTransportMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all registered OmemoMucMessageListeners of an incoming OmemoMessageElement in a MUC.
|
* Notify all registered OmemoMucMessageListeners of an incoming OmemoMessageElement in a MUC.
|
||||||
*
|
*
|
||||||
* @param muc MultiUserChat the message was received in.
|
* @param muc MultiUserChat the message was received in.
|
||||||
* @param stanza Original Stanza.
|
* @param stanza Original Stanza.
|
||||||
* @param decryptedMessage Decryped OmemoMessage.
|
* @param decryptedMessage Decrypted OmemoMessage.
|
||||||
*/
|
*/
|
||||||
void notifyOmemoMucMessageReceived(MultiUserChat muc,
|
void notifyOmemoMucMessageReceived(MultiUserChat muc,
|
||||||
Stanza stanza,
|
Stanza stanza,
|
||||||
|
@ -817,18 +823,21 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify registered OmemoMucMessageReceived listeners about KeyTransportMessages sent in a MUC.
|
* Notify all registered OmemoMessageListeners of an incoming OMEMO encrypted Carbon Copy.
|
||||||
|
* Remember: If you want to receive OMEMO encrypted carbon copies, you have to enable carbons using
|
||||||
|
* {@link CarbonManager#enableCarbons()}.
|
||||||
*
|
*
|
||||||
* @param muc MultiUserChat in which the message was sent
|
* @param direction direction of the carbon copy
|
||||||
* @param stanza original stanza
|
* @param carbonCopy carbon copy itself
|
||||||
* @param decryptedKeyTransportMessage decrypted OMEMO KeyTransportElement
|
* @param wrappingMessage wrapping message
|
||||||
|
* @param decryptedCarbonCopy decrypted carbon copy OMEMO element
|
||||||
*/
|
*/
|
||||||
void notifyOmemoMucKeyTransportMessageReceived(MultiUserChat muc,
|
void notifyOmemoCarbonCopyReceived(CarbonExtension.Direction direction,
|
||||||
Stanza stanza,
|
Message carbonCopy,
|
||||||
OmemoMessage.Received decryptedKeyTransportMessage)
|
Message wrappingMessage,
|
||||||
{
|
OmemoMessage.Received decryptedCarbonCopy) {
|
||||||
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
for (OmemoMessageListener l : omemoMessageListeners) {
|
||||||
l.onOmemoKeyTransportReceived(muc, stanza, decryptedKeyTransportMessage);
|
l.onOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, decryptedCarbonCopy);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,8 @@ import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
|
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
public class OmemoMessage {
|
public class OmemoMessage {
|
||||||
|
|
||||||
private final OmemoElement element;
|
private final OmemoElement element;
|
||||||
|
@ -42,43 +44,91 @@ public class OmemoMessage {
|
||||||
this.iv = iv;
|
this.iv = iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the original OmemoElement (<encrypted/>).
|
||||||
|
*
|
||||||
|
* @return omemoElement
|
||||||
|
*/
|
||||||
public OmemoElement getElement() {
|
public OmemoElement getElement() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the messageKey (or transported key in case of a KeyTransportMessage).
|
||||||
|
*
|
||||||
|
* @return key
|
||||||
|
*/
|
||||||
public byte[] getKey() {
|
public byte[] getKey() {
|
||||||
return messageKey.clone();
|
return messageKey.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the initialization vector belonging to the key.
|
||||||
|
* @return initialization vector
|
||||||
|
*/
|
||||||
public byte[] getIv() {
|
public byte[] getIv() {
|
||||||
return iv.clone();
|
return iv.clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Outgoing OMEMO message.
|
||||||
|
*/
|
||||||
public static class Sent extends OmemoMessage {
|
public static class Sent extends OmemoMessage {
|
||||||
private final ArrayList<OmemoDevice> intendedDevices = new ArrayList<>();
|
private final ArrayList<OmemoDevice> intendedDevices = new ArrayList<>();
|
||||||
private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
|
private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new outgoing OMEMO message.
|
||||||
|
* @param element OmemoElement
|
||||||
|
* @param key messageKey (or transported key)
|
||||||
|
* @param iv initialization vector belonging to key
|
||||||
|
* @param intendedDevices devices the client intended to encrypt the message for
|
||||||
|
* @param skippedDevices devices which were skipped during encryption process because encryption
|
||||||
|
* failed for some reason
|
||||||
|
*/
|
||||||
Sent(OmemoElement element, byte[] key, byte[] iv, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
|
Sent(OmemoElement element, byte[] key, byte[] iv, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
|
||||||
super(element, key, iv);
|
super(element, key, iv);
|
||||||
this.intendedDevices.addAll(intendedDevices);
|
this.intendedDevices.addAll(intendedDevices);
|
||||||
this.skippedDevices.putAll(skippedDevices);
|
this.skippedDevices.putAll(skippedDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArrayList<OmemoDevice> getIntendedDevices() {
|
/**
|
||||||
|
* Return a list of all devices the sender originally intended to encrypt the message for.
|
||||||
|
* @return list of intended recipients.
|
||||||
|
*/
|
||||||
|
public List<OmemoDevice> getIntendedDevices() {
|
||||||
return intendedDevices;
|
return intendedDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a map of all skipped recipients and the reasons for skipping.
|
||||||
|
* @return map of skipped recipients and reasons for that.
|
||||||
|
*/
|
||||||
public HashMap<OmemoDevice, Throwable> getSkippedDevices() {
|
public HashMap<OmemoDevice, Throwable> getSkippedDevices() {
|
||||||
return skippedDevices;
|
return skippedDevices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determine, if some recipients were skipped during encryption.
|
||||||
|
* @return true if recipients were skipped.
|
||||||
|
*/
|
||||||
public boolean isMissingRecipients() {
|
public boolean isMissingRecipients() {
|
||||||
return !getSkippedDevices().isEmpty();
|
return !getSkippedDevices().isEmpty();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Message asMessage() {
|
/**
|
||||||
|
* Return the OmemoElement wrapped in a Message ready to be sent.
|
||||||
|
* The message is addressed to recipient, contains the OmemoElement
|
||||||
|
* as well as an optional clear text hint as body, a MAM storage hint
|
||||||
|
* and an EME hint about OMEMO encryption.
|
||||||
|
*
|
||||||
|
* @param recipient recipient for the to-field of the message.
|
||||||
|
* @return Message
|
||||||
|
*/
|
||||||
|
public Message asMessage(Jid recipient) {
|
||||||
|
|
||||||
Message messageStanza = new Message();
|
Message messageStanza = new Message();
|
||||||
|
messageStanza.setTo(recipient);
|
||||||
messageStanza.addExtension(getElement());
|
messageStanza.addExtension(getElement());
|
||||||
|
|
||||||
if (OmemoConfiguration.getAddOmemoHintBody()) {
|
if (OmemoConfiguration.getAddOmemoHintBody()) {
|
||||||
|
@ -92,54 +142,72 @@ public class OmemoMessage {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Incoming OMEMO message.
|
||||||
|
*/
|
||||||
public static class Received extends OmemoMessage {
|
public static class Received extends OmemoMessage {
|
||||||
private final String message;
|
private final String message;
|
||||||
private final OmemoFingerprint sendersFingerprint;
|
private final OmemoFingerprint sendersFingerprint;
|
||||||
private final OmemoDevice senderDevice;
|
private final OmemoDevice senderDevice;
|
||||||
private final CARBON carbon;
|
|
||||||
private final boolean preKeyMessage;
|
private final boolean preKeyMessage;
|
||||||
|
|
||||||
Received(OmemoElement element, byte[] key, byte[] iv, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon, boolean preKeyMessage) {
|
/**
|
||||||
|
* Create a new incoming OMEMO message.
|
||||||
|
* @param element original OmemoElement
|
||||||
|
* @param key message key (or transported key)
|
||||||
|
* @param iv respective initialization vector
|
||||||
|
* @param body decrypted body
|
||||||
|
* @param sendersFingerprint OmemoFingerprint of the senders identityKey
|
||||||
|
* @param senderDevice OmemoDevice of the sender
|
||||||
|
* @param preKeyMessage if this was a preKeyMessage or not
|
||||||
|
*/
|
||||||
|
Received(OmemoElement element, byte[] key, byte[] iv, String body, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, boolean preKeyMessage) {
|
||||||
super(element, key, iv);
|
super(element, key, iv);
|
||||||
this.message = message;
|
this.message = body;
|
||||||
this.sendersFingerprint = sendersFingerprint;
|
this.sendersFingerprint = sendersFingerprint;
|
||||||
this.senderDevice = senderDevice;
|
this.senderDevice = senderDevice;
|
||||||
this.carbon = carbon;
|
|
||||||
this.preKeyMessage = preKeyMessage;
|
this.preKeyMessage = preKeyMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
/**
|
||||||
|
* Return the decrypted body of the message.
|
||||||
|
* @return decrypted body
|
||||||
|
*/
|
||||||
|
public String getBody() {
|
||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the fingerprint of the messages sender device.
|
||||||
|
* @return fingerprint of sender
|
||||||
|
*/
|
||||||
public OmemoFingerprint getSendersFingerprint() {
|
public OmemoFingerprint getSendersFingerprint() {
|
||||||
return sendersFingerprint;
|
return sendersFingerprint;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the OmemoDevice which sent the message.
|
||||||
|
* @return senderDevice
|
||||||
|
*/
|
||||||
public OmemoDevice getSenderDevice() {
|
public OmemoDevice getSenderDevice() {
|
||||||
return senderDevice;
|
return senderDevice;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the carbon type.
|
* Return true, if this message was sent as a preKeyMessage.
|
||||||
*
|
* @return preKeyMessage or not
|
||||||
* @return carbon type
|
|
||||||
*/
|
*/
|
||||||
public CARBON getCarbon() {
|
|
||||||
return carbon;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isPreKeyMessage() {
|
boolean isPreKeyMessage() {
|
||||||
return preKeyMessage;
|
return preKeyMessage;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Types of Carbon Messages.
|
* Return true, if the message was a KeyTransportMessage.
|
||||||
|
* A KeyTransportMessage is a OmemoMessage without a payload.
|
||||||
|
* @return keyTransportMessage?
|
||||||
*/
|
*/
|
||||||
public enum CARBON {
|
public boolean isKeyTransportMessage() {
|
||||||
NONE, //No carbon
|
return message == null;
|
||||||
SENT, //Sent carbon
|
}
|
||||||
RECV //Received Carbon
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -435,8 +435,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
*/
|
*/
|
||||||
private OmemoMessage.Received decryptMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
private OmemoMessage.Received decryptMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||||
BareJid senderJid,
|
BareJid senderJid,
|
||||||
OmemoElement omemoElement,
|
OmemoElement omemoElement)
|
||||||
OmemoMessage.CARBON carbon)
|
|
||||||
throws CorruptedOmemoKeyException, CryptoFailedException, NoRawSessionException
|
throws CorruptedOmemoKeyException, CryptoFailedException, NoRawSessionException
|
||||||
{
|
{
|
||||||
OmemoManager manager = managerGuard.get();
|
OmemoManager manager = managerGuard.get();
|
||||||
|
@ -459,12 +458,12 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
String plaintext = OmemoRatchet.decryptMessageElement(omemoElement, cipherAndAuthTag);
|
String plaintext = OmemoRatchet.decryptMessageElement(omemoElement, cipherAndAuthTag);
|
||||||
|
|
||||||
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||||
plaintext, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
plaintext, senderFingerprint, senderDevice, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// KeyTransportMessages don't require decryption of the payload.
|
// KeyTransportMessages don't require decryption of the payload.
|
||||||
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||||
null, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
null, senderFingerprint, senderDevice, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1027,7 +1026,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @param userDevice our OmemoDevice
|
* @param userDevice our OmemoDevice
|
||||||
* @param devices collection of OmemoDevices
|
* @param devices collection of OmemoDevices
|
||||||
*/
|
*/
|
||||||
private static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
||||||
if (devices.contains(userDevice)) {
|
if (devices.contains(userDevice)) {
|
||||||
devices.remove(userDevice);
|
devices.remove(userDevice);
|
||||||
}
|
}
|
||||||
|
@ -1074,13 +1073,69 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
};
|
};
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage, OmemoManager.LoggedInOmemoManager omemoManager) {
|
public void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction,
|
||||||
// TODO: implement
|
Message carbonCopy,
|
||||||
|
Message wrappingMessage,
|
||||||
|
OmemoManager.LoggedInOmemoManager managerGuard) {
|
||||||
|
OmemoManager manager = managerGuard.get();
|
||||||
|
// Avoid the ratchet being manipulated and the bundle being published multiple times simultaneously
|
||||||
|
synchronized (manager) {
|
||||||
|
OmemoDevice userDevice = manager.getOwnDevice();
|
||||||
|
OmemoElement element = carbonCopy.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
|
||||||
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OmemoMessage.Received decrypted;
|
||||||
|
BareJid sender = carbonCopy.getFrom().asBareJid();
|
||||||
|
|
||||||
|
try {
|
||||||
|
decrypted = decryptMessage(managerGuard, sender, element);
|
||||||
|
manager.notifyOmemoCarbonCopyReceived(direction, carbonCopy, wrappingMessage, decrypted);
|
||||||
|
|
||||||
|
if (decrypted.isPreKeyMessage() && OmemoConfiguration.getCompleteSessionWithEmptyMessage()) {
|
||||||
|
LOGGER.log(Level.FINE, "Received a preKeyMessage in a carbon copy from " + decrypted.getSenderDevice() + ".\n" +
|
||||||
|
"Complete the session by sending an empty response message.");
|
||||||
|
try {
|
||||||
|
sendRatchetUpdate(managerGuard, decrypted.getSenderDevice());
|
||||||
|
} catch (CannotEstablishOmemoSessionException e) {
|
||||||
|
throw new AssertionError("Since we successfully received a message, we MUST be able to " +
|
||||||
|
"establish a session. " + e);
|
||||||
|
} catch (NoSuchAlgorithmException | InterruptedException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Cannot send a ratchet update message.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (NoRawSessionException e) {
|
||||||
|
OmemoDevice device = e.getDeviceWithoutSession();
|
||||||
|
LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);
|
||||||
|
|
||||||
|
if (OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages()) {
|
||||||
|
repairBrokenSessionWithPreKeyMessage(managerGuard, device);
|
||||||
|
}
|
||||||
|
} catch (CorruptedOmemoKeyException | CryptoFailedException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not decrypt incoming carbon copy: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Upload fresh bundle.
|
||||||
|
if (getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size() < OmemoConstants.PRE_KEY_COUNT_PER_BUNDLE) {
|
||||||
|
LOGGER.log(Level.FINE, "We used up a preKey. Upload a fresh bundle.");
|
||||||
|
try {
|
||||||
|
getOmemoStoreBackend().replenishKeys(userDevice);
|
||||||
|
OmemoBundleElement bundleElement = getOmemoStoreBackend().packOmemoBundle(userDevice);
|
||||||
|
publishBundle(manager.getConnection(), userDevice, bundleElement);
|
||||||
|
} catch (CorruptedOmemoKeyException | InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not republish replenished bundle.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) {
|
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) {
|
||||||
OmemoManager manager = managerGuard.get();
|
OmemoManager manager = managerGuard.get();
|
||||||
|
// Avoid the ratchet being manipulated and the bundle being published multiple times simultaneously
|
||||||
|
synchronized (manager.LOCK) {
|
||||||
|
OmemoDevice userDevice = manager.getOwnDevice();
|
||||||
OmemoElement element = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
|
OmemoElement element = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
|
||||||
if (element == null) {
|
if (element == null) {
|
||||||
return;
|
return;
|
||||||
|
@ -1104,37 +1159,52 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
sender = occupantJid.asBareJid();
|
sender = occupantJid.asBareJid();
|
||||||
|
|
||||||
// try is for this
|
// try is for this
|
||||||
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
decrypted = decryptMessage(managerGuard, sender, element);
|
||||||
|
|
||||||
if (element.isMessageElement()) {
|
|
||||||
manager.notifyOmemoMucMessageReceived(muc, stanza, decrypted);
|
manager.notifyOmemoMucMessageReceived(muc, stanza, decrypted);
|
||||||
} else {
|
|
||||||
manager.notifyOmemoMucKeyTransportMessageReceived(muc, stanza, decrypted);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
sender = stanza.getFrom().asBareJid();
|
sender = stanza.getFrom().asBareJid();
|
||||||
|
|
||||||
// and this
|
// and this
|
||||||
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
decrypted = decryptMessage(managerGuard, sender, element);
|
||||||
|
|
||||||
if (element.isMessageElement()) {
|
|
||||||
manager.notifyOmemoMessageReceived(stanza, decrypted);
|
manager.notifyOmemoMessageReceived(stanza, decrypted);
|
||||||
} else {
|
}
|
||||||
manager.notifyOmemoKeyTransportMessageReceived(stanza, decrypted);
|
|
||||||
|
if (decrypted.isPreKeyMessage() && OmemoConfiguration.getCompleteSessionWithEmptyMessage()) {
|
||||||
|
LOGGER.log(Level.FINE, "Received a preKeyMessage from " + decrypted.getSenderDevice() + ".\n" +
|
||||||
|
"Complete the session by sending an empty response message.");
|
||||||
|
try {
|
||||||
|
sendRatchetUpdate(managerGuard, decrypted.getSenderDevice());
|
||||||
|
} catch (CannotEstablishOmemoSessionException e) {
|
||||||
|
throw new AssertionError("Since we successfully received a message, we MUST be able to " +
|
||||||
|
"establish a session. " + e);
|
||||||
|
} catch (NoSuchAlgorithmException | InterruptedException | SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Cannot send a ratchet update message.", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} catch (NoRawSessionException e) {
|
||||||
catch (NoRawSessionException e) {
|
|
||||||
OmemoDevice device = e.getDeviceWithoutSession();
|
OmemoDevice device = e.getDeviceWithoutSession();
|
||||||
LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);
|
LOGGER.log(Level.WARNING, "No raw session found for contact " + device + ". ", e);
|
||||||
|
|
||||||
if (OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages()) {
|
if (OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages()) {
|
||||||
repairBrokenSessionWithPreKeyMessage(managerGuard, device);
|
repairBrokenSessionWithPreKeyMessage(managerGuard, device);
|
||||||
}
|
}
|
||||||
}
|
} catch (CorruptedOmemoKeyException | CryptoFailedException e) {
|
||||||
catch (CorruptedOmemoKeyException | CryptoFailedException e) {
|
|
||||||
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Upload fresh bundle.
|
||||||
|
if (getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size() < OmemoConstants.PRE_KEY_COUNT_PER_BUNDLE) {
|
||||||
|
LOGGER.log(Level.FINE, "We used up a preKey. Upload a fresh bundle.");
|
||||||
|
try {
|
||||||
|
getOmemoStoreBackend().replenishKeys(userDevice);
|
||||||
|
OmemoBundleElement bundleElement = getOmemoStoreBackend().packOmemoBundle(userDevice);
|
||||||
|
publishBundle(manager.getConnection(), userDevice, bundleElement);
|
||||||
|
} catch (CorruptedOmemoKeyException | InterruptedException | SmackException.NoResponseException | SmackException.NotConnectedException | XMPPException.XMPPErrorException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not republish replenished bundle.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1152,11 +1222,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
try {
|
try {
|
||||||
// Create fresh session and send new preKeyMessage.
|
// Create fresh session and send new preKeyMessage.
|
||||||
buildFreshSessionWithDevice(manager.getConnection(), manager.getOwnDevice(), brokenDevice);
|
buildFreshSessionWithDevice(manager.getConnection(), manager.getOwnDevice(), brokenDevice);
|
||||||
OmemoElement ratchetUpdate = createRatchetUpdateElement(managerGuard, brokenDevice);
|
sendRatchetUpdate(managerGuard, brokenDevice);
|
||||||
Message m = new Message();
|
|
||||||
m.setTo(brokenDevice.getJid());
|
|
||||||
m.addExtension(ratchetUpdate);
|
|
||||||
manager.getConnection().sendStanza(m);
|
|
||||||
|
|
||||||
} catch (CannotEstablishOmemoSessionException | CorruptedOmemoKeyException e) {
|
} catch (CannotEstablishOmemoSessionException | CorruptedOmemoKeyException e) {
|
||||||
LOGGER.log(Level.WARNING, "Unable to repair session with " + brokenDevice, e);
|
LOGGER.log(Level.WARNING, "Unable to repair session with " + brokenDevice, e);
|
||||||
|
@ -1167,6 +1233,31 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send an empty OMEMO message to contactsDevice in order to forward the ratchet.
|
||||||
|
* @param managerGuard
|
||||||
|
* @param contactsDevice
|
||||||
|
* @throws CorruptedOmemoKeyException if our or their OMEMO key is corrupted.
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
* @throws NoSuchAlgorithmException if AES encryption fails
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws CryptoFailedException if encryption fails (should not happen though, but who knows...)
|
||||||
|
* @throws CannotEstablishOmemoSessionException if we cannot establish a session with contactsDevice.
|
||||||
|
*/
|
||||||
|
private void sendRatchetUpdate(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
|
||||||
|
throws CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException,
|
||||||
|
NoSuchAlgorithmException, SmackException.NotConnectedException, CryptoFailedException,
|
||||||
|
CannotEstablishOmemoSessionException
|
||||||
|
{
|
||||||
|
OmemoManager manager = managerGuard.get();
|
||||||
|
OmemoElement ratchetUpdate = createRatchetUpdateElement(managerGuard, contactsDevice);
|
||||||
|
Message m = new Message();
|
||||||
|
m.setTo(contactsDevice.getJid());
|
||||||
|
m.addExtension(ratchetUpdate);
|
||||||
|
manager.getConnection().sendStanza(m);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the joined MUC with EntityBareJid jid, or null if its not a room and/or not joined.
|
* Return the joined MUC with EntityBareJid jid, or null if its not a room and/or not joined.
|
||||||
* @param connection xmpp connection
|
* @param connection xmpp connection
|
||||||
|
|
|
@ -16,7 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.listener;
|
package org.jivesoftware.smackx.omemo.listener;
|
||||||
|
|
||||||
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||||
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -33,11 +35,8 @@ public interface OmemoMessageListener {
|
||||||
*/
|
*/
|
||||||
void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage);
|
void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage);
|
||||||
|
|
||||||
/**
|
void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction,
|
||||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
Message carbonCopy,
|
||||||
*
|
Message wrappingMessage,
|
||||||
* @param stanza received (encrypted) stanza.
|
OmemoMessage.Received decryptedCarbonCopy);
|
||||||
* @param decryptedKeyTransportMessage decrypted OMEMO key transport message.
|
|
||||||
*/
|
|
||||||
void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,13 +33,4 @@ public interface OmemoMucMessageListener {
|
||||||
* @param decryptedOmemoMessage decrypted Omemo message
|
* @param decryptedOmemoMessage decrypted Omemo message
|
||||||
*/
|
*/
|
||||||
void onOmemoMucMessageReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedOmemoMessage);
|
void onOmemoMucMessageReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedOmemoMessage);
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
|
||||||
*
|
|
||||||
* @param muc MultiUserChat the message was sent in
|
|
||||||
* @param stanza original stanza
|
|
||||||
* @param decryptedKeyTransportElement decrypted KeyTransportMessage
|
|
||||||
*/
|
|
||||||
void onOmemoKeyTransportReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedKeyTransportElement);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,9 @@
|
||||||
package org.jivesoftware.smackx.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
@ -40,18 +42,6 @@ public class OmemoConfigurationTest {
|
||||||
assertEquals("FileBasedOmemoStoreDefaultPath must equal the one we set.", storePath.getAbsolutePath(),
|
assertEquals("FileBasedOmemoStoreDefaultPath must equal the one we set.", storePath.getAbsolutePath(),
|
||||||
OmemoConfiguration.getFileBasedOmemoStoreDefaultPath().getAbsolutePath());
|
OmemoConfiguration.getFileBasedOmemoStoreDefaultPath().getAbsolutePath());
|
||||||
|
|
||||||
// EME
|
|
||||||
OmemoConfiguration.setAddEmeEncryptionHint(false);
|
|
||||||
assertEquals(false, OmemoConfiguration.getAddEmeEncryptionHint());
|
|
||||||
OmemoConfiguration.setAddEmeEncryptionHint(true);
|
|
||||||
assertEquals(true, OmemoConfiguration.getAddEmeEncryptionHint());
|
|
||||||
|
|
||||||
// MAM
|
|
||||||
OmemoConfiguration.setAddMAMStorageProcessingHint(false);
|
|
||||||
assertEquals(false, OmemoConfiguration.getAddMAMStorageProcessingHint());
|
|
||||||
OmemoConfiguration.setAddMAMStorageProcessingHint(true);
|
|
||||||
assertEquals(true, OmemoConfiguration.getAddMAMStorageProcessingHint());
|
|
||||||
|
|
||||||
// Body hint
|
// Body hint
|
||||||
OmemoConfiguration.setAddOmemoHintBody(false);
|
OmemoConfiguration.setAddOmemoHintBody(false);
|
||||||
assertEquals(false, OmemoConfiguration.getAddOmemoHintBody());
|
assertEquals(false, OmemoConfiguration.getAddOmemoHintBody());
|
||||||
|
@ -107,5 +97,17 @@ public class OmemoConfigurationTest {
|
||||||
} catch (IllegalArgumentException e) {
|
} catch (IllegalArgumentException e) {
|
||||||
// Expected
|
// Expected
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Repair broken sessions
|
||||||
|
OmemoConfiguration.setRepairBrokenSessionsWithPrekeyMessages(false);
|
||||||
|
assertFalse(OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages());
|
||||||
|
OmemoConfiguration.setRepairBrokenSessionsWithPrekeyMessages(true);
|
||||||
|
assertTrue(OmemoConfiguration.getRepairBrokenSessionsWithPreKeyMessages());
|
||||||
|
|
||||||
|
// Complete fresh sessions
|
||||||
|
OmemoConfiguration.setCompleteSessionWithEmptyMessage(false);
|
||||||
|
assertFalse(OmemoConfiguration.getCompleteSessionWithEmptyMessage());
|
||||||
|
OmemoConfiguration.setCompleteSessionWithEmptyMessage(true);
|
||||||
|
assertTrue(OmemoConfiguration.getCompleteSessionWithEmptyMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,7 @@ import static junit.framework.TestCase.assertFalse;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
|
@ -34,6 +35,16 @@ public class OmemoServiceTest extends SmackTestSuite {
|
||||||
private static final int IGNORE_STALE = OmemoConfiguration.getIgnoreStaleDevicesAfterHours();
|
private static final int IGNORE_STALE = OmemoConfiguration.getIgnoreStaleDevicesAfterHours();
|
||||||
private static final int DELETE_STALE = OmemoConfiguration.getDeleteStaleDevicesAfterHours();
|
private static final int DELETE_STALE = OmemoConfiguration.getDeleteStaleDevicesAfterHours();
|
||||||
|
|
||||||
|
@Test(expected = IllegalStateException.class)
|
||||||
|
public void getInstanceFailsWhenNullTest() {
|
||||||
|
OmemoService.getInstance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void isServiceRegisteredTest() {
|
||||||
|
assertFalse(OmemoService.isServiceRegistered());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Test correct functionality of isStale method.
|
* Test correct functionality of isStale method.
|
||||||
* @throws XmppStringprepException
|
* @throws XmppStringprepException
|
||||||
|
@ -57,5 +68,24 @@ public class OmemoServiceTest extends SmackTestSuite {
|
||||||
|
|
||||||
// Own device is never stale, no matter how old
|
// Own device is never stale, no matter how old
|
||||||
assertFalse(OmemoService.isStale(user, user, deleteMe, DELETE_STALE));
|
assertFalse(OmemoService.isStale(user, user, deleteMe, DELETE_STALE));
|
||||||
|
|
||||||
|
// Always return false if date is null.
|
||||||
|
assertFalse(OmemoService.isStale(user, other, null, DELETE_STALE));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void removeOurDeviceTest() throws XmppStringprepException {
|
||||||
|
OmemoDevice a = new OmemoDevice(JidCreate.bareFrom("a@b.c"), 123);
|
||||||
|
OmemoDevice b = new OmemoDevice(JidCreate.bareFrom("a@b.c"), 124);
|
||||||
|
|
||||||
|
HashSet<OmemoDevice> devices = new HashSet<>();
|
||||||
|
devices.add(a); devices.add(b);
|
||||||
|
|
||||||
|
assertTrue(devices.contains(a));
|
||||||
|
assertTrue(devices.contains(b));
|
||||||
|
OmemoService.removeOurDevice(a, devices);
|
||||||
|
|
||||||
|
assertFalse(devices.contains(a));
|
||||||
|
assertTrue(devices.contains(b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue