/** * * Copyright 2017 Florian Schmaus, 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 junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertNotSame; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.cleanServerSideTraces; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.setUpOmemoManager; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.subscribe; import static org.jivesoftware.smackx.omemo.OmemoIntegrationTestHelper.unidirectionalTrust; import java.security.NoSuchAlgorithmException; import java.util.logging.Level; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.chat2.ChatManager; import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smackx.omemo.element.OmemoBundleElement; import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag; import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation; import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener; import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubException.NotAPubSubNodeException; import junit.framework.TestCase; 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; /** * Test message sending. */ public class OmemoMessageSendingTest extends AbstractOmemoIntegrationTest { private OmemoManager alice, bob; private OmemoStore store; public OmemoMessageSendingTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, TestNotPossibleException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException { super(environment); } @Override public void before() { alice = OmemoManager.getInstanceFor(conOne, 123); bob = OmemoManager.getInstanceFor(conTwo, 345); store = OmemoService.getInstance().getOmemoStoreBackend(); } /** * This Test tests sending and receiving messages. * Alice and Bob create fresh devices, then they add another to their rosters. * Next they build sessions with one another and Alice sends a message to Bob. * After receiving and successfully decrypting the message, its tested, if Bob * publishes a new Bundle. After that Bob replies to the message and its tested, * whether Alice can decrypt the message and if she does NOT publish a new Bundle. * * @throws CorruptedOmemoKeyException * @throws InterruptedException * @throws SmackException.NoResponseException * @throws SmackException.NotConnectedException * @throws XMPPException.XMPPErrorException * @throws SmackException.NotLoggedInException * @throws PubSubException.NotALeafNodeException * @throws CannotEstablishOmemoSessionException * @throws UndecidedOmemoIdentityException * @throws NoSuchAlgorithmException * @throws CryptoFailedException * @throws NotAPubSubNodeException */ @SmackIntegrationTest public void messageSendingTest() throws CorruptedOmemoKeyException, InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException, XMPPException.XMPPErrorException, SmackException.NotLoggedInException, PubSubException.NotALeafNodeException, CannotEstablishOmemoSessionException, UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException, PubSubException.NotAPubSubNodeException { final String alicesSecret = "Hey Bob! I love you!"; final String bobsSecret = "I love you too, Alice."; //aww <3 final SimpleResultSyncPoint messageOneSyncPoint = new SimpleResultSyncPoint(); final SimpleResultSyncPoint messageTwoSyncPoint = new SimpleResultSyncPoint(); // Subscribe to one another subscribe(alice, bob, "Bob"); subscribe(bob, alice,"Alice"); // initialize OmemoManagers setUpOmemoManager(alice); setUpOmemoManager(bob); // Save initial bundles OmemoBundleElement aliceBundle = store.packOmemoBundle(alice); OmemoBundleElement bobsBundle = store.packOmemoBundle(bob); // Trust unidirectionalTrust(alice, bob); unidirectionalTrust(bob, alice); // Register messageListeners bob.addOmemoMessageListener(new OmemoMessageListener() { @Override public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) { LOGGER.log(Level.INFO,"Bob received message: " + decryptedBody); if (decryptedBody.trim().equals(alicesSecret.trim())) { messageOneSyncPoint.signal(); } else { messageOneSyncPoint.signal(new Exception("Received message must equal sent message.")); } } @Override public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) { } }); alice.addOmemoMessageListener(new OmemoMessageListener() { @Override public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) { LOGGER.log(Level.INFO, "Alice received message: " + decryptedBody); if (decryptedBody.trim().equals(bobsSecret.trim())) { messageTwoSyncPoint.signal(); } else { messageTwoSyncPoint.signal(new Exception("Received message must equal sent message.")); } } @Override public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) { } }); // Prepare Alice message for Bob Message encryptedA = alice.encrypt(bob.getOwnJid(), alicesSecret); ChatManager.getInstanceFor(alice.getConnection()).chatWith(bob.getOwnJid().asEntityBareJidIfPossible()) .send(encryptedA); try { messageOneSyncPoint.waitForResult(10 * 1000); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Exception while waiting for message: " + e, e); TestCase.fail("Bob must have received Alice message."); } // Check if Bob published a new Bundle assertNotSame("Bob must have published another bundle at this point, since we used a PreKeyMessage.", bobsBundle, OmemoService.fetchBundle(alice, bob.getOwnDevice())); // Prepare Bobs response Message encryptedB = bob.encrypt(alice.getOwnJid(), bobsSecret); ChatManager.getInstanceFor(bob.getConnection()).chatWith(alice.getOwnJid().asEntityBareJidIfPossible()) .send(encryptedB); try { messageTwoSyncPoint.waitForResult(10 * 1000); } catch (Exception e) { LOGGER.log(Level.SEVERE, "Exception while waiting for response: " + e, e); TestCase.fail("Alice must have received a response from Bob."); } assertEquals("Alice must not have published a new bundle, since we built the session using Bobs bundle.", aliceBundle, OmemoService.fetchBundle(bob, alice.getOwnDevice())); } @Override public void after() { alice.shutdown(); bob.shutdown(); cleanServerSideTraces(alice); cleanServerSideTraces(bob); } }