diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java new file mode 100644 index 000000000..69d611ea4 --- /dev/null +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/ReadOnlyDeviceIntegrationTest.java @@ -0,0 +1,72 @@ +/** + * + * 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 static junit.framework.TestCase.assertEquals; +import static junit.framework.TestCase.assertFalse; +import static junit.framework.TestCase.assertTrue; + +import org.jivesoftware.smack.SmackException; +import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; +import org.jivesoftware.smackx.omemo.exceptions.ReadOnlyDeviceException; +import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; + +import org.igniterealtime.smack.inttest.SmackIntegrationTest; +import org.igniterealtime.smack.inttest.SmackIntegrationTestEnvironment; +import org.igniterealtime.smack.inttest.TestNotPossibleException; + +public class ReadOnlyDeviceIntegrationTest extends AbstractTwoUsersOmemoIntegrationTest { + + public ReadOnlyDeviceIntegrationTest(SmackIntegrationTestEnvironment environment) throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, TestNotPossibleException { + super(environment); + } + + @SmackIntegrationTest + public void test() throws InterruptedException, SmackException.NoResponseException, SmackException.NotLoggedInException, SmackException.NotConnectedException, CryptoFailedException, UndecidedOmemoIdentityException { + boolean prevIgnoreReadOnlyConf = OmemoConfiguration.getIgnoreReadOnlyDevices(); + int prevMaxMessageCounter = OmemoConfiguration.getMaxReadOnlyMessageCount(); + + OmemoConfiguration.setIgnoreReadOnlyDevices(true); + // Set the maxReadOnlyMessageCount to ridiculously low threshold of 5. + // This means that Alice will be able to encrypt 5 messages for Bob, while the 6th will not be encrypted for Bob. + OmemoConfiguration.setMaxReadOnlyMessageCount(5); + + // Reset counter to begin test + alice.getOmemoService().getOmemoStoreBackend().storeOmemoMessageCounter(alice.getOwnDevice(), bob.getOwnDevice(), 0); + + // Since the max threshold is 5, we must be able to encrypt 5 messages for Bob. + for (int i = 0; i < 5; i++) { + assertEquals(i, alice.getOmemoService().getOmemoStoreBackend().loadOmemoMessageCounter(alice.getOwnDevice(), bob.getOwnDevice())); + OmemoMessage.Sent message = alice.encrypt(bob.getOwnJid(), "Hello World!"); + assertFalse(message.getSkippedDevices().containsKey(bob.getOwnDevice())); + } + + // Now the message counter must be too high and Bobs device must be skipped. + OmemoMessage.Sent message = alice.encrypt(bob.getOwnJid(), "Hello World!"); + Throwable exception = message.getSkippedDevices().get(bob.getOwnDevice()); + assertTrue(exception instanceof ReadOnlyDeviceException); + assertEquals(bob.getOwnDevice(), ((ReadOnlyDeviceException) exception).getDevice()); + + // Reset the message counter + alice.getOmemoService().getOmemoStoreBackend().storeOmemoMessageCounter(alice.getOwnDevice(), bob.getOwnDevice(), 0); + + // Reset the configuration to previous values + OmemoConfiguration.setMaxReadOnlyMessageCount(prevMaxMessageCounter); + OmemoConfiguration.setIgnoreReadOnlyDevices(prevIgnoreReadOnlyConf); + } +} diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoConfiguration.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoConfiguration.java index 8756ef37e..c53f97dac 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoConfiguration.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoConfiguration.java @@ -30,7 +30,7 @@ public final class OmemoConfiguration { * Set to true, in order to ignore read-only devices. * * @param ignore ignore read-only devices - * @see Blog Post explaining the danger of read-only devices. + * @see Blog Post explaining the danger of read-only devices. TODO: Add URL */ public static void setIgnoreReadOnlyDevices(boolean ignore) { IGNORE_READ_ONLY_DEVICES = ignore; @@ -40,7 +40,7 @@ public final class OmemoConfiguration { * Return true, if the client should stop encrypting messages to a read-only device. * * @return true if read-only devices should get ignored after a certain amount of unanswered messages. - * @see Blog Post explaining the danger of read-only devices. + * @see Blog Post explaining the danger of read-only devices. TODO: Add URL */ public static boolean getIgnoreReadOnlyDevices() { return IGNORE_READ_ONLY_DEVICES; @@ -53,7 +53,7 @@ public final class OmemoConfiguration { * This threshold is used to prevent read-only devices from weakening forward secrecy. * * @param maxReadOnlyMessageCount maximum number of allowed messages to a read-only device. - * @see Blog Post explaining the danger of read-only devices. + * @see Blog Post explaining the danger of read-only devices. TODO: Add URL */ public static void setMaxReadOnlyMessageCount(int maxReadOnlyMessageCount) { if (maxReadOnlyMessageCount <= 0) { @@ -69,7 +69,7 @@ public final class OmemoConfiguration { * This threshold is used to prevent read-only devices from weakening forward secrecy. * * @return maximum number of allowed messages to a read-only device. - * @see Blog Post explaining the danger of read-only devices. + * @see Blog Post explaining the danger of read-only devices. TODO: Add URL */ public static int getMaxReadOnlyMessageCount() { return MAX_READ_ONLY_MESSAGE_COUNT; diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java index f8d38706d..27f775f3e 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoService.java @@ -19,9 +19,6 @@ package org.jivesoftware.smackx.omemo; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYLENGTH; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE; -import javax.crypto.BadPaddingException; -import javax.crypto.IllegalBlockSizeException; -import javax.crypto.NoSuchPaddingException; import java.io.UnsupportedEncodingException; import java.security.InvalidAlgorithmParameterException; import java.security.InvalidKeyException; @@ -39,6 +36,9 @@ import java.util.Random; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; +import javax.crypto.BadPaddingException; +import javax.crypto.IllegalBlockSizeException; +import javax.crypto.NoSuchPaddingException; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection;