From 7bff6e7d995216c40c3a2e8e1d37ffa7973b6ba6 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 31 Dec 2017 16:55:25 +0100 Subject: [PATCH] Fix some issues and delete old code. --- .../smackx/omemo/OmemoManagerSetupHelper.java | 15 +- .../omemo/signal/SignalOmemoRatchet.java | 2 +- .../omemo/signal/SignalOmemoService.java | 14 +- .../signal/SignalOmemoStoreConnector.java | 9 +- .../smackx/omemo/OmemoManager.java | 19 +- .../smackx/omemo/OmemoService.java | 26 +- .../smackx/omemo/OmemoServiceOld.java | 1323 ----------------- .../jivesoftware/smackx/omemo/OmemoStore.java | 26 +- .../omemo/util/OmemoMessageBuilder.java | 1 + .../smackx/omemo/WrapperObjectsTest.java | 30 +- 10 files changed, 76 insertions(+), 1389 deletions(-) delete mode 100644 smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoServiceOld.java diff --git a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java index f9c6feb33..da0368c89 100644 --- a/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java +++ b/smack-integration-test/src/main/java/org/jivesoftware/smackx/omemo/OmemoManagerSetupHelper.java @@ -25,7 +25,7 @@ import java.util.HashMap; import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; -import org.jivesoftware.smack.omemo.util.EphemeralTrustCallback; +import org.jivesoftware.smackx.omemo.util.EphemeralTrustCallback; import org.jivesoftware.smack.packet.Presence; import org.jivesoftware.smack.roster.PresenceEventListener; import org.jivesoftware.smack.roster.Roster; @@ -98,8 +98,8 @@ public class OmemoManagerSetupHelper { public static void trustAllIdentities(OmemoManager alice, OmemoManager bob) throws InterruptedException, SmackException.NotConnectedException, SmackException.NotLoggedInException, - SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException - { + SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, + XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException { Roster roster = Roster.getInstanceFor(alice.getConnection()); if (alice.getOwnJid() != bob.getOwnJid() && @@ -118,8 +118,8 @@ public class OmemoManagerSetupHelper { public static void trustAllIdentitiesWithTests(OmemoManager alice, OmemoManager bob) throws InterruptedException, SmackException.NotConnectedException, SmackException.NotLoggedInException, - SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException - { + SmackException.NoResponseException, CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, + XMPPException.XMPPErrorException, PubSubException.NotALeafNodeException { alice.requestDeviceListUpdateFor(bob.getOwnJid()); HashMap fps1 = alice.getActiveFingerprints(bob.getOwnJid()); @@ -177,7 +177,10 @@ public class OmemoManagerSetupHelper { } } - public static void cleanUpPubSub(OmemoManager omemoManager) throws SmackException.NotLoggedInException { + public static void cleanUpPubSub(OmemoManager omemoManager) + throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException, + PubSubException.NotALeafNodeException + { PubSubManager pm = PubSubManager.getInstance(omemoManager.getConnection(),omemoManager.getOwnJid()); try { omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid()); diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java index d1bfafbcb..f82f6acc5 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoRatchet.java @@ -68,7 +68,7 @@ public class SignalOmemoRatchet SessionCipher> store) { super(omemoManager, store); - this.storeConnector = new SignalOmemoStoreConnector(omemoManager, store); + this.storeConnector = new SignalOmemoStoreConnector(omemoManager.getOwnDevice(), store); } @Override diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoService.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoService.java index 889203430..cdbcc4473 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoService.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoService.java @@ -111,17 +111,17 @@ public final class SignalOmemoService } @Override - protected void processBundle(OmemoManager.LoggedInOmemoManager managerGuard, - PreKeyBundle preKeyBundle, - OmemoDevice device) + protected void processBundle(OmemoDevice userDevice, + PreKeyBundle contactsBundle, + OmemoDevice contactsDevice) throws CorruptedOmemoKeyException { - SignalOmemoStoreConnector connector = new SignalOmemoStoreConnector(managerGuard.get(), getOmemoStoreBackend()); + SignalOmemoStoreConnector connector = new SignalOmemoStoreConnector(userDevice, getOmemoStoreBackend()); SessionBuilder builder = new SessionBuilder(connector, connector, connector, connector, - SignalOmemoStoreConnector.asAddress(device)); + SignalOmemoStoreConnector.asAddress(contactsDevice)); try { - builder.process(preKeyBundle); - LOGGER.log(Level.FINE, "Session built with " + device); + builder.process(contactsBundle); + LOGGER.log(Level.FINE, "Session built with " + contactsDevice); } catch (org.whispersystems.libsignal.InvalidKeyException e) { throw new CorruptedOmemoKeyException(e.getMessage()); } catch (UntrustedIdentityException e) { diff --git a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java index 921bc855a..ea078dae2 100644 --- a/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java +++ b/smack-omemo-signal/src/main/java/org/jivesoftware/smackx/omemo/signal/SignalOmemoStoreConnector.java @@ -26,7 +26,6 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; -import org.jivesoftware.smackx.omemo.OmemoManager; import org.jivesoftware.smackx.omemo.OmemoStore; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.internal.OmemoDevice; @@ -59,20 +58,20 @@ public class SignalOmemoStoreConnector private static final Logger LOGGER = Logger.getLogger(SignalOmemoStoreConnector.class.getName()); - private final OmemoManager omemoManager; private final OmemoStore omemoStore; + private final OmemoDevice userDevice; - public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore store) { - this.omemoManager = omemoManager; + this.userDevice = userDevice; this.omemoStore = store; } OmemoDevice getOurDevice() { - return omemoManager.getOwnDevice(); + return userDevice; } @Override diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java index aa7d756d4..62cb8f356 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoManager.java @@ -538,8 +538,8 @@ public final class OmemoManager extends Manager { public OmemoFingerprint getFingerprint(OmemoDevice device) throws CannotEstablishOmemoSessionException, SmackException.NotLoggedInException, - CorruptedOmemoKeyException - { + CorruptedOmemoKeyException, SmackException.NotConnectedException, InterruptedException, + SmackException.NoResponseException { synchronized (LOCK) { if (getOwnJid() == null) { throw new SmackException.NotLoggedInException(); @@ -554,13 +554,22 @@ public final class OmemoManager extends Manager { } /** - * Return all fingerprints of active devices of a contact. + * Return all OmemoFingerprints of active devices of a contact. + * TODO: Make more fail-safe * @param contact contact - * @return HashMap of deviceIds and corresponding fingerprints. + * @return Map of all active devices of the contact and their fingerprints. + * + * @throws SmackException.NotLoggedInException + * @throws CorruptedOmemoKeyException + * @throws CannotEstablishOmemoSessionException + * @throws SmackException.NotConnectedException + * @throws InterruptedException + * @throws SmackException.NoResponseException */ public HashMap getActiveFingerprints(BareJid contact) throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, - CannotEstablishOmemoSessionException + CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, + SmackException.NoResponseException { synchronized (LOCK) { if (getOwnJid() == null) { 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 a2a3aa64c..0e39e0dc9 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 @@ -42,6 +42,9 @@ import org.jivesoftware.smack.SmackException; import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPException; +import org.jivesoftware.smack.packet.Message; +import org.jivesoftware.smack.packet.Stanza; +import org.jivesoftware.smackx.carbons.packet.CarbonExtension; import org.jivesoftware.smackx.omemo.element.OmemoBundleElement; import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement; import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl; @@ -317,7 +320,7 @@ public abstract class OmemoService(bundlesList.values()).get(randomIndex); // build the session - processBundle(connection, userDevice, randomPreKeyBundle, contactsDevice); + processBundle(userDevice, randomPreKeyBundle, contactsDevice); } /** @@ -813,21 +816,19 @@ public abstract class OmemoService IdentityKeyPair class - * @param IdentityKey class - * @param PreKey class - * @param SignedPreKey class - * @param Session class - * @param Address class - * @param Elliptic Curve PublicKey class - * @param Bundle class - * @param Cipher class - * @author Paul Schaub - */ -public abstract class OmemoServiceOld - implements OmemoCarbonCopyStanzaReceivedListener, OmemoMessageStanzaReceivedListener -{ - static { - Security.addProvider(new BouncyCastleProvider()); - } - - protected static final Logger LOGGER = Logger.getLogger(OmemoService.class.getName()); - - /** - * This is a singleton. - */ - private static OmemoServiceOld INSTANCE; - - protected OmemoStore omemoStore; - protected final HashMap> omemoRatchets = new HashMap<>(); - - /** - * Return the singleton instance of this class. When no instance is set, throw an IllegalStateException instead. - * @return instance. - */ - public static OmemoServiceOld getInstance() { - if (INSTANCE == null) { - throw new IllegalStateException("No OmemoService registered"); - } - return INSTANCE; - } - - /** - * Set singleton instance. Throws an IllegalStateException, if there is already a service set as instance. - * - * @param omemoService instance - */ - protected static void setInstance(OmemoServiceOld omemoService) { - if (INSTANCE != null) { - throw new IllegalStateException("An OmemoService is already registered"); - } - INSTANCE = omemoService; - } - - /** - * Returns true, if an instance of the service singleton is set. Otherwise return false. - * - * @return true, if instance is not null. - */ - public static boolean isServiceRegistered() { - return INSTANCE != null; - } - - /** - * Return the used omemoStore backend. - * If there is no store backend set yet, set the default one (typically a file-based one). - * - * @return omemoStore backend - */ - public OmemoStore - getOmemoStoreBackend() { - if (omemoStore == null) { - omemoStore = createDefaultOmemoStoreBackend(); - } - return omemoStore; - } - - /** - * Set an omemoStore as backend. Throws an IllegalStateException, if there is already a backend set. - * - * @param omemoStore store. - */ - public void setOmemoStoreBackend( - OmemoStore omemoStore) { - if (this.omemoStore != null) { - throw new IllegalStateException("An OmemoStore backend has already been set."); - } - this.omemoStore = omemoStore; - } - - /** - * Create a default OmemoStore object. - * - * @return default omemoStore. - */ - public abstract OmemoStore - createDefaultOmemoStoreBackend(); - - /** - * Create a new OmemoService object. This should only happen once. - * When the service gets created, it tries a placeholder crypto function in order to test, if all necessary - * algorithms are available on the system. - * - * @throws NoSuchPaddingException When no Cipher could be instantiated. - * @throws NoSuchAlgorithmException when no Cipher could be instantiated. - * @throws NoSuchProviderException when BouncyCastle could not be found. - * @throws InvalidAlgorithmParameterException when the Cipher could not be initialized - * @throws InvalidKeyException when the generated key is invalid - * @throws UnsupportedEncodingException when UTF8 is unavailable - * @throws BadPaddingException when cipher.doFinal gets wrong padding - * @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size. - */ - public OmemoServiceOld() - throws NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException, - BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException - { - // Check availability of algorithms and encodings needed for crypto - checkAvailableAlgorithms(); - } - - /** - * Makes the OmemoManager known to the OmemoService. - * @param manager manager. - */ - void registerManager(OmemoManager manager) { - omemoRatchets.put(manager, instantiateOmemoRatchet(manager, getOmemoStoreBackend())); - } - - /** - * Initialize OMEMO functionality for OmemoManager omemoManager. - * - * @param managerGuard OmemoManager we'd like to initialize. - * @throws InterruptedException - * @throws CorruptedOmemoKeyException - * @throws XMPPException.XMPPErrorException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException - * @throws PubSubException.NotALeafNodeException - */ - void publish(OmemoManager.LoggedInOmemoManager managerGuard) - throws InterruptedException, CorruptedOmemoKeyException, XMPPException.XMPPErrorException, - SmackException.NotConnectedException, SmackException.NoResponseException, - PubSubException.NotALeafNodeException - { - OmemoManager manager = managerGuard.get(); - OmemoDevice userDevice = manager.getOwnDevice(); - - // Create new keys if necessary and publish to the server. - publishBundle(managerGuard); - - // Get fresh device list from server - OmemoCachedDeviceList list = refreshOwnDeviceList(managerGuard); - if (!list.isActive(userDevice.getDeviceId())) { - list.addDevice(userDevice.getDeviceId()); - } - - publishDeviceIds(managerGuard, new OmemoDeviceListElement_VAxolotl(list.getActiveDevices())); - } - - /** - * Test availability of required algorithms. We do this in advance, so we can simplify exception handling later. - * - * @throws NoSuchPaddingException - * @throws UnsupportedEncodingException - * @throws InvalidAlgorithmParameterException - * @throws NoSuchAlgorithmException - * @throws IllegalBlockSizeException - * @throws BadPaddingException - * @throws NoSuchProviderException - * @throws InvalidKeyException - */ - protected static void checkAvailableAlgorithms() - throws NoSuchPaddingException, UnsupportedEncodingException, InvalidAlgorithmParameterException, - NoSuchAlgorithmException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, - InvalidKeyException - { - // Test crypto functions. The method below throws an exception, when crypto algorithms are missing. - new OmemoMessageBuilder<>(null, null, ""); - } - - /** - * Publish a bundle to the server. - * - * @param managerGuard OmemoManager - * @throws SmackException.NotConnectedException - * @throws InterruptedException - * @throws SmackException.NoResponseException - * @throws CorruptedOmemoKeyException - * @throws XMPPException.XMPPErrorException - */ - void publishBundle(OmemoManager.LoggedInOmemoManager managerGuard) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CorruptedOmemoKeyException, XMPPException.XMPPErrorException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - Date lastSignedPreKeyRenewal = getOmemoStoreBackend().getDateOfLastSignedPreKeyRenewal(userDevice); - if (OmemoConfiguration.getRenewOldSignedPreKeys() && lastSignedPreKeyRenewal != null) { - if (System.currentTimeMillis() - lastSignedPreKeyRenewal.getTime() - > 1000L * 60 * 60 * OmemoConfiguration.getRenewOldSignedPreKeysAfterHours()) { - LOGGER.log(Level.FINE, "Renewing signedPreKey"); - getOmemoStoreBackend().changeSignedPreKey(userDevice); - } - } else { - getOmemoStoreBackend().setDateOfLastSignedPreKeyRenewal(userDevice, new Date()); - } - - // publish - OmemoBundleElement bundleElement = getOmemoStoreBackend().packOmemoBundle(userDevice); - - PubSubManager.getInstance(omemoManager.getConnection(), omemoManager.getOwnJid()) - .tryToPublishAndPossibleAutoCreate( - OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(userDevice.getDeviceId()), - new PayloadItem<>(bundleElement)); - } - - /** - * Publish the given deviceList to the server. - * - * @param managerGuard OmemoManager - * @param deviceList list of deviceIDs - * @throws InterruptedException Exception - * @throws XMPPException.XMPPErrorException Exception - * @throws SmackException.NotConnectedException Exception - * @throws SmackException.NoResponseException Exception - * @throws PubSubException.NotALeafNodeException Exception - */ - static void publishDeviceIds(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDeviceListElement deviceList) - throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, - SmackException.NoResponseException, PubSubException.NotALeafNodeException - { - OmemoManager omemoManager = managerGuard.get(); - PubSubManager.getInstance(omemoManager.getConnection(), omemoManager.getOwnJid()) - .tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList)); - } - - /** - * Directly fetch the device list of a contact. - * - * @param managerGuard OmemoManager - * @param contact BareJid of the contact - * @return The OmemoDeviceListElement of the contact - * @throws XMPPException.XMPPErrorException When - * @throws SmackException.NotConnectedException something - * @throws InterruptedException goes - * @throws SmackException.NoResponseException wrong - * @throws PubSubException.NotALeafNodeException when the device lists node is not a LeafNode - * @throws NotAPubSubNodeException - */ - static OmemoDeviceListElement fetchDeviceList(OmemoManager.LoggedInOmemoManager managerGuard, BareJid contact) - throws InterruptedException, SmackException.NotConnectedException, SmackException.NoResponseException, - XMPPException.XMPPErrorException { - OmemoManager omemoManager = managerGuard.get(); - try { - LeafNode node = PubSubManager.getInstance(omemoManager.getConnection(), contact).getLeafNode(PEP_NODE_DEVICE_LIST); - return extractDeviceListFrom(node); - } - - catch (XMPPException.XMPPErrorException e) { - - if (e.getXMPPError().getCondition() == XMPPError.Condition.item_not_found) { - LOGGER.log(Level.WARNING, "Could not refresh own deviceList, because the node did not exist: " - + e.getMessage()); - } - - throw e; - - } catch (PubSubException.NotALeafNodeException e) { - LOGGER.log(Level.WARNING, "Could not refresh own deviceList, because the Node is not a LeafNode: " + - e.getMessage()); - } - - catch (PubSubException.NotAPubSubNodeException e) { - LOGGER.log(Level.WARNING, "Caught a NotAPubSubNodeException when fetching a deviceList node. " + - "This probably means that we're dealing with an ejabberd server and the LeafNode does not exist."); - } - return null; - } - - /** - * Refresh our deviceList from the server. - * - * @param managerGuard omemoManager - * @return true, if we should publish our device list again (because its broken or not existent or - * doesn't contain our id...) - * - * @throws SmackException.NotConnectedException - * @throws InterruptedException - * @throws SmackException.NoResponseException - */ - private OmemoCachedDeviceList refreshOwnDeviceList(OmemoManager.LoggedInOmemoManager managerGuard) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - XMPPException.XMPPErrorException - { - return refreshDeviceList(managerGuard, managerGuard.get().getOwnJid()); - } - - /** - * Refresh the deviceList of contact and merge it with the one stored locally. - * @param managerGuard omemoManager - * @param contact contact - * @throws SmackException.NotConnectedException - * @throws InterruptedException - * @throws SmackException.NoResponseException - */ - OmemoCachedDeviceList refreshDeviceList(OmemoManager.LoggedInOmemoManager managerGuard, BareJid contact) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - OmemoDeviceListElement list = null; - try { - list = fetchDeviceList(managerGuard, contact); - } catch (XMPPException.XMPPErrorException e) { - LOGGER.log(Level.WARNING, "Could not refresh DeviceList of " + contact + ".", e); - } - return getOmemoStoreBackend().mergeCachedDeviceList(userDevice, contact, list); - } - - /** - * Fetch the OmemoBundleElement of the contact. - * - * @param managerGuard OmemoManager - * @param contact the contacts BareJid - * @return the OmemoBundleElement of the contact - * @throws XMPPException.XMPPErrorException When - * @throws SmackException.NotConnectedException something - * @throws InterruptedException goes - * @throws SmackException.NoResponseException wrong - * @throws PubSubException.NotALeafNodeException when the bundles node is not a LeafNode - * @throws NotAPubSubNodeException - */ - static OmemoBundleElement fetchBundle(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contact) - throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, - SmackException.NoResponseException, PubSubException.NotALeafNodeException, NotAPubSubNodeException - { - OmemoManager omemoManager = managerGuard.get(); - LeafNode node = PubSubManager.getInstance(omemoManager.getConnection(), contact.getJid()).getLeafNode( - PEP_NODE_BUNDLE_FROM_DEVICE_ID(contact.getDeviceId())); - - if (node == null) { - return null; - } - - List> bundleItems = node.getItems(); - if (bundleItems.isEmpty()) { - return null; - } - - return bundleItems.get(bundleItems.size() - 1).getPayload(); - } - - /** - * Extract the OmemoDeviceListElement of a contact from a node containing his OmemoDeviceListElement. - * This method also ensures, that the node only contains one item. If there are more than one item in the node, - * all items are deleted and the list gets published again. - * - * @param node typically a LeafNode containing the OmemoDeviceListElement of a contact - * @return the extracted OmemoDeviceListElement. - * @throws XMPPException.XMPPErrorException When - * @throws SmackException.NotConnectedException something - * @throws InterruptedException goes - * @throws SmackException.NoResponseException wrong - */ - private static OmemoDeviceListElement extractDeviceListFrom(LeafNode node) - throws XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, - SmackException.NoResponseException - { - if (node == null) { - LOGGER.log(Level.WARNING, "DeviceListNode is null."); - return null; - } - - List> items = node.getItems(); - if (items.size() > 0) { - - OmemoDeviceListElement listElement = items.get(items.size() - 1).getPayload(); - - if (items.size() > 1) { - node.deleteAllItems(); - node.publish(new PayloadItem<>(listElement)); - } - - return listElement; - } - - Set emptySet = Collections.emptySet(); - return new OmemoDeviceListElement_VAxolotl(emptySet); - } - - /** - * Build sessions for all devices of the contacts in the list, of which we have no session yet. - * When there are devices, this method throws a {@link CannotEstablishOmemoSessionException} which contains all - * those devices, while still establishing sessions with all other devices. - * - * @param managerGuard manager - * @param jids list of BareJids - * @throws SmackException.NotConnectedException - * @throws InterruptedException - * @throws SmackException.NoResponseException - * @throws CannotEstablishOmemoSessionException if we cannot create sessions with some devices. - */ - void buildMissingOmemoSessions(OmemoManager.LoggedInOmemoManager managerGuard, List jids) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CannotEstablishOmemoSessionException { - - CannotEstablishOmemoSessionException ex = null; - - for (BareJid jid : jids) { - try { - buildMissingOmemoSessions(managerGuard, jid); - } catch (CannotEstablishOmemoSessionException e) { - if (ex == null) { - ex = e; - } else { - ex.addFailures(e); - } - } - } - - if (ex != null) { - throw ex; - } - } - - /** - * Build sessions for all devices of the contact that we do not have a session with yet. - * - * @param managerGuard omemoManager - * @param jid the BareJid of the contact - */ - void buildMissingOmemoSessions(OmemoManager.LoggedInOmemoManager managerGuard, BareJid jid) - throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CannotEstablishOmemoSessionException { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - OmemoCachedDeviceList devices = getOmemoStoreBackend().loadCachedDeviceList(userDevice, jid); - CannotEstablishOmemoSessionException sessionException = null; - - if (devices == null || devices.getAllDevices().isEmpty()) { - refreshDeviceList(managerGuard, jid); - devices = getOmemoStoreBackend().loadCachedDeviceList(userDevice, jid); - } - - for (int id : devices.getActiveDevices()) { - - OmemoDevice device = new OmemoDevice(jid, id); - if (getOmemoStoreBackend().containsRawSession(userDevice, device)) { - // We have a session already. - continue; - } - - // Build missing session - try { - buildSessionWithDevice(managerGuard, device, false); - } catch (CannotEstablishOmemoSessionException e) { - - if (sessionException == null) { - sessionException = e; - } else { - sessionException.addFailures(e); - } - - } catch (CorruptedOmemoKeyException e) { - CannotEstablishOmemoSessionException fail = - new CannotEstablishOmemoSessionException(device, e); - - if (sessionException == null) { - sessionException = fail; - } else { - sessionException.addFailures(fail); - } - } - } - - if (sessionException != null) { - throw sessionException; - } - } - - /** - * Build an OmemoSession for the given OmemoDevice. - * - * @param managerGuard omemoManager - * @param contactsDevice OmemoDevice - * @param fresh Do we want to build a session even if we already have one? - * @throws CannotEstablishOmemoSessionException when no session could be established - * @throws CorruptedOmemoKeyException when the bundle contained an invalid OMEMO identityKey - */ - public void buildSessionWithDevice(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice contactsDevice, - boolean fresh) - throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - if (contactsDevice.equals(omemoManager.getOwnDevice())) { - return; - } - - // Do not build sessions with devices we already know... - if (!fresh && getOmemoStoreBackend().containsRawSession(userDevice, contactsDevice)) { - return; - } - - OmemoBundleElement bundle; - try { - bundle = fetchBundle(managerGuard, contactsDevice); - } catch (SmackException | XMPPException.XMPPErrorException | InterruptedException e) { - throw new CannotEstablishOmemoSessionException(contactsDevice, e); - } - - HashMap bundles = getOmemoStoreBackend().keyUtil().BUNDLE.bundles(bundle, contactsDevice); - - // Select random Bundle - int randomIndex = new Random().nextInt(bundles.size()); - T_Bundle randomPreKeyBundle = new ArrayList<>(bundles.values()).get(randomIndex); - // Build raw session - processBundle(managerGuard, randomPreKeyBundle, contactsDevice); - } - - - - /** - * Process a received message. Try to decrypt it in case we are a recipient device. If we are not a recipient - * device, return null. - * - * @param contactsDevice OmemoDevice of the sender of the message - * @param omemoElement the encrypted message - * @param information OmemoMessageInformation object which will contain meta data about the decrypted message - * @return decrypted message or null - * @throws NoRawSessionException - * @throws InterruptedException - * @throws SmackException.NoResponseException - * @throws SmackException.NotConnectedException - * @throws CryptoFailedException - * @throws XMPPException.XMPPErrorException - * @throws CorruptedOmemoKeyException - */ - private Message processReceivingMessage(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice contactsDevice, - OmemoElement omemoElement, - final OmemoMessageInformation information) - throws NoRawSessionException, InterruptedException, SmackException.NoResponseException, - SmackException.NotConnectedException, CryptoFailedException, XMPPException.XMPPErrorException, - CorruptedOmemoKeyException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - ArrayList messageRecipientKeys = omemoElement.getHeader().getKeys(); - - // Do we have a key with our ID in the message? - for (OmemoKeyElement k : messageRecipientKeys) { - // Only decrypt with our deviceID - if (k.getId() != omemoManager.getDeviceId()) { - continue; - } - - Message decrypted = decryptOmemoMessageElement(managerGuard, contactsDevice, omemoElement, information); - if (contactsDevice.equals(omemoManager.getOwnJid()) && decrypted != null) { - getOmemoStoreBackend().setDateOfLastReceivedMessage(userDevice, contactsDevice, new Date()); - } - return decrypted; - } - - LOGGER.log(Level.INFO, "There is no key with our deviceId " + omemoManager.getDeviceId() + ". Silently discard the message."); - return null; - } - - /** - * Decrypt a given OMEMO encrypted message. Return null, if there is no OMEMO element in the message, - * otherwise try to decrypt the message and return a ClearTextMessage object. - * - * @param managerGuard omemoManager of the receiving device - * @param sender barejid of the sender - * @param message encrypted message - * @return decrypted message or null - * @throws InterruptedException Exception - * @throws SmackException.NoResponseException Exception - * @throws SmackException.NotConnectedException Exception - * @throws CryptoFailedException When the message could not be decrypted. - * @throws XMPPException.XMPPErrorException Exception - * @throws CorruptedOmemoKeyException When the used OMEMO keys are invalid. - * @throws NoRawSessionException When there is no session to decrypt the message with in the double - * ratchet library - */ - ClearTextMessage processLocalMessage(OmemoManager.LoggedInOmemoManager managerGuard, - BareJid sender, - Message message) - throws InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException, - CryptoFailedException, XMPPException.XMPPErrorException, CorruptedOmemoKeyException, NoRawSessionException - { - if (OmemoManager.stanzaContainsOmemoElement(message)) { - OmemoElement omemoMessageElement = message.getExtension(OmemoElement.NAME_ENCRYPTED, OMEMO_NAMESPACE_V_AXOLOTL); - OmemoMessageInformation info = new OmemoMessageInformation(); - Message decrypted = processReceivingMessage(managerGuard, - new OmemoDevice(sender, omemoMessageElement.getHeader().getSid()), - omemoMessageElement, info); - return new ClearTextMessage(decrypted != null ? decrypted.getBody() : null, message, info); - } else { - LOGGER.log(Level.WARNING, "Stanza does not contain an OMEMO message."); - return null; - } - } - - /** - * Encrypt a clear text message for the given recipient. - * The body of the message will be encrypted. - * - * @param managerGuard omemoManager of the sending device - * @param recipient BareJid of the recipient - * @param message message to encrypt. - * @return OmemoMessageElement - * @throws CryptoFailedException - * @throws UndecidedOmemoIdentityException - * @throws NoSuchAlgorithmException - */ - OmemoElement processSendingMessage(OmemoManager.LoggedInOmemoManager managerGuard, - BareJid recipient, - Message message) - throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException, - SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CannotEstablishOmemoSessionException - { - ArrayList recipients = new ArrayList<>(); - recipients.add(recipient); - return processSendingMessage(managerGuard, recipients, message); - } - - /** - * Encrypt a clear text message for the given recipients. - * The body of the message will be encrypted. - * - * @param managerGuard omemoManager of the sending device. - * @param recipients List of BareJids of all recipients - * @param message message to encrypt. - * @return OmemoMessageElement - * @throws CryptoFailedException - * @throws UndecidedOmemoIdentityException - * @throws NoSuchAlgorithmException - */ - OmemoElement processSendingMessage(OmemoManager.LoggedInOmemoManager managerGuard, - ArrayList recipients, - Message message) - throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException, - SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, - CannotEstablishOmemoSessionException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - CannotEstablishOmemoSessionException sessionException = null; - // Them - The contact wants to read the message on all their devices. - HashMap> receivers = new HashMap<>(); - for (BareJid recipient : recipients) { - try { - buildMissingOmemoSessions(managerGuard, recipient); - } catch (CannotEstablishOmemoSessionException e) { - - if (sessionException == null) { - sessionException = e; - } else { - sessionException.addFailures(e); - } - } - } - - for (BareJid recipient : recipients) { - OmemoCachedDeviceList theirDevices = getOmemoStoreBackend().loadCachedDeviceList(userDevice, recipient); - ArrayList receivingDevices = new ArrayList<>(); - for (int id : theirDevices.getActiveDevices()) { - OmemoDevice recipientDevice = new OmemoDevice(recipient, id); - - if (getOmemoStoreBackend().containsRawSession(userDevice, recipientDevice)) { - receivingDevices.add(recipientDevice); - } - - if (sessionException != null) { - sessionException.addSuccess(recipientDevice); - } - } - - if (!receivingDevices.isEmpty()) { - receivers.put(recipient, receivingDevices); - } - } - - // Us - We want to read the message on all of our devices - OmemoCachedDeviceList ourDevices = getOmemoStoreBackend().loadCachedDeviceList(userDevice, omemoManager.getOwnJid()); - if (ourDevices == null) { - ourDevices = new OmemoCachedDeviceList(); - } - - ArrayList ourReceivingDevices = new ArrayList<>(); - for (int id : ourDevices.getActiveDevices()) { - OmemoDevice remoteUserDevice = new OmemoDevice(omemoManager.getOwnJid(), id); - if (id == omemoManager.getDeviceId()) { - // Don't build session with our exact device. - continue; - } - - Date lastReceived = getOmemoStoreBackend().getDateOfLastReceivedMessage(userDevice, remoteUserDevice); - if (lastReceived == null) { - getOmemoStoreBackend().setDateOfLastReceivedMessage(userDevice, remoteUserDevice, new Date()); - lastReceived = new Date(); - } - - if (OmemoConfiguration.getIgnoreStaleDevices() && System.currentTimeMillis() - lastReceived.getTime() - > 1000L * 60 * 60 * OmemoConfiguration.getIgnoreStaleDevicesAfterHours()) { - LOGGER.log(Level.WARNING, "Refusing to encrypt message for stale device " + remoteUserDevice + - " which was inactive for at least " + OmemoConfiguration.getIgnoreStaleDevicesAfterHours() + - " hours."); - } else { - - if (getOmemoStoreBackend().containsRawSession(userDevice, remoteUserDevice)) { - ourReceivingDevices.add(remoteUserDevice); - } - } - } - - if (!ourReceivingDevices.isEmpty()) { - receivers.put(omemoManager.getOwnJid(), ourReceivingDevices); - } - - if (sessionException != null && sessionException.requiresThrowing()) { - throw sessionException; - } - - return encryptOmemoMessage(managerGuard, receivers, message); - } - - /** - * Decrypt a incoming OmemoMessageElement that was sent by the OmemoDevice 'from'. - * - * @param managerGuard omemoManager of the decrypting device. - * @param from OmemoDevice that sent the message - * @param message Encrypted OmemoMessageElement - * @param information OmemoMessageInformation object which will contain metadata about the encryption - * @return Decrypted message - * @throws CryptoFailedException when decrypting message fails for some reason - * @throws InterruptedException - * @throws CorruptedOmemoKeyException - * @throws XMPPException.XMPPErrorException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException - * @throws NoRawSessionException - */ - private Message decryptOmemoMessageElement(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice from, - OmemoElement message, - final OmemoMessageInformation information) - throws CryptoFailedException, InterruptedException, CorruptedOmemoKeyException, - XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException, - NoRawSessionException - { - CipherAndAuthTag transportedKey = decryptTransportedOmemoKey(managerGuard, from, message, information); - return OmemoRatchet.decryptMessageElement(message, transportedKey); - } - - /** - * Decrypt a messageKey that was transported in an OmemoElement. - * - * @param managerGuard omemoManager of the receiving device. - * @param sender omemoDevice of the sender. - * @param omemoMessage omemoElement containing the key. - * @param messageInfo omemoMessageInformation that will contain metadata about the encryption. - * @return a CipherAndAuthTag pair - * @throws CryptoFailedException - * @throws NoRawSessionException - * @throws InterruptedException - * @throws CorruptedOmemoKeyException - * @throws XMPPException.XMPPErrorException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException - */ - private CipherAndAuthTag decryptTransportedOmemoKey(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice sender, - OmemoElement omemoMessage, - OmemoMessageInformation messageInfo) - throws CryptoFailedException, NoRawSessionException, InterruptedException, CorruptedOmemoKeyException, - XMPPException.XMPPErrorException, SmackException.NotConnectedException, SmackException.NoResponseException - { - OmemoManager omemoManager = managerGuard.get(); - OmemoDevice userDevice = omemoManager.getOwnDevice(); - - int preKeyCountBefore = getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size(); - - CipherAndAuthTag cipherAndAuthTag = omemoRatchets.get(managerGuard.get()).retrieveMessageKeyAndAuthTag(sender, omemoMessage); - - messageInfo.setSenderDevice(sender); - messageInfo.setSenderFingerprint(omemoStore.keyUtil().getFingerprintOfIdentityKey( - omemoStore.loadOmemoIdentityKey(userDevice, sender))); - - if (preKeyCountBefore != getOmemoStoreBackend().loadOmemoPreKeys(userDevice).size()) { - LOGGER.log(Level.FINE, "We used up a preKey. Publish new Bundle."); - publishBundle(managerGuard); - } - return cipherAndAuthTag; - } - - /** - * Encrypt the message and return it as an OmemoMessageElement. - * - * @param managerGuard omemoManager of the encrypting device. - * @param recipients List of devices that will be able to decipher the message. - * @param message Clear text message - * - * @throws CryptoFailedException when some cryptographic function fails - * @throws UndecidedOmemoIdentityException when the identity of one or more contacts is undecided - * - * @return OmemoMessageElement - */ - OmemoElement encryptOmemoMessage(OmemoManager.LoggedInOmemoManager managerGuard, - HashMap> recipients, - Message message) - throws CryptoFailedException, UndecidedOmemoIdentityException - { - OmemoMessageBuilder - builder; - try { - builder = new OmemoMessageBuilder<>(managerGuard, getOmemoRatchet(managerGuard.get()), message.getBody()); - } catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | NoSuchProviderException | - NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) { - throw new CryptoFailedException(e); - } - - UndecidedOmemoIdentityException undecided = null; - - for (Map.Entry> entry : recipients.entrySet()) { - for (OmemoDevice c : entry.getValue()) { - try { - builder.addRecipient(c); - } catch (CorruptedOmemoKeyException | CannotEstablishOmemoSessionException e) { - // TODO: How to react? - LOGGER.log(Level.SEVERE, "encryptOmemoMessage failed to establish a session with device " - + c + ": " + e.getMessage()); - } catch (UndecidedOmemoIdentityException e) { - // Collect all undecided devices - if (undecided == null) { - undecided = e; - } else { - undecided.join(e); - } - } - } - } - - if (undecided != null) { - throw undecided; - } - - return builder.finish(); - } - - /** - * Prepares a keyTransportElement with a random aes key and iv. - * - * @param managerGuard omemoManager of the sending device. - * @param recipients recipients of the omemoKeyTransportElement - * @return KeyTransportElement - * @throws CryptoFailedException - * @throws UndecidedOmemoIdentityException - * @throws CorruptedOmemoKeyException - * @throws CannotEstablishOmemoSessionException - */ - OmemoElement prepareOmemoKeyTransportElement(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice... recipients) - throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException, - CannotEstablishOmemoSessionException - { - OmemoMessageBuilder - builder; - try { - builder = new OmemoMessageBuilder<>(managerGuard, getOmemoRatchet(managerGuard.get()), null); - - } catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | NoSuchProviderException | - NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) { - throw new CryptoFailedException(e); - } - - for (OmemoDevice r : recipients) { - builder.addRecipient(r); - } - - return builder.finish(); - } - - /** - * Prepare a KeyTransportElement with aesKey and iv. - * - * @param managerGuard OmemoManager of the sending device. - * @param aesKey AES key - * @param iv initialization vector - * @param recipients recipients - * @return KeyTransportElement - * @throws CryptoFailedException - * @throws UndecidedOmemoIdentityException - * @throws CorruptedOmemoKeyException - * @throws CannotEstablishOmemoSessionException - */ - OmemoElement prepareOmemoKeyTransportElement(OmemoManager.LoggedInOmemoManager managerGuard, - byte[] aesKey, - byte[] iv, - OmemoDevice... recipients) - throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException, - CannotEstablishOmemoSessionException - { - OmemoMessageBuilder - builder; - try { - builder = new OmemoMessageBuilder<>(managerGuard, getOmemoRatchet(managerGuard.get()), aesKey, iv); - - } catch (UnsupportedEncodingException | BadPaddingException | IllegalBlockSizeException | NoSuchProviderException | - NoSuchPaddingException | InvalidAlgorithmParameterException | InvalidKeyException | NoSuchAlgorithmException e) { - throw new CryptoFailedException(e); - } - - for (OmemoDevice r : recipients) { - builder.addRecipient(r); - } - - return builder.finish(); - } - - /** - * Return a new RatchetUpdateMessage. - * - * @param managerGuard omemoManager of the sending device. - * @param recipient recipient - * @param preKeyMessage if true, a new session will be built for this message (useful to repair broken sessions) - * otherwise the message will be encrypted using the existing session. - * @return OmemoRatchetUpdateMessage - * @throws CannotEstablishOmemoSessionException - * @throws CorruptedOmemoKeyException - * @throws CryptoFailedException - * @throws UndecidedOmemoIdentityException - */ - protected Message getOmemoRatchetUpdateMessage(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice recipient, - boolean preKeyMessage) - throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException, CryptoFailedException, - UndecidedOmemoIdentityException - { - if (preKeyMessage) { - buildSessionWithDevice(managerGuard, recipient, true); - } - - OmemoElement keyTransportElement = prepareOmemoKeyTransportElement(managerGuard, recipient); - Message ratchetUpdateMessage = managerGuard.get().finishMessage(keyTransportElement); - ratchetUpdateMessage.setTo(recipient.getJid()); - - return ratchetUpdateMessage; - } - - /** - * Send an OmemoRatchetUpdateMessage to recipient. If preKeyMessage is true, the message will be encrypted using a - * freshly built session. This can be used to repair broken sessions. - * - * @param managerGuard omemoManager of the sending device. - * @param recipient recipient - * @param preKeyMessage shall this be a preKeyMessage? - * @throws UndecidedOmemoIdentityException - * @throws CorruptedOmemoKeyException - * @throws CryptoFailedException - * @throws CannotEstablishOmemoSessionException - */ - protected void sendOmemoRatchetUpdateMessage(OmemoManager.LoggedInOmemoManager managerGuard, - OmemoDevice recipient, - boolean preKeyMessage) - throws UndecidedOmemoIdentityException, CorruptedOmemoKeyException, CryptoFailedException, - CannotEstablishOmemoSessionException - { - Message ratchetUpdateMessage = getOmemoRatchetUpdateMessage(managerGuard, recipient, preKeyMessage); - - try { - managerGuard.get().getConnection().sendStanza(ratchetUpdateMessage); - - } catch (SmackException.NotConnectedException | InterruptedException e) { - LOGGER.log(Level.WARNING, "sendOmemoRatchetUpdateMessage failed: " + e.getMessage()); - } - } - - /** - * Try to decrypt a mamQueryResult. Note that OMEMO messages can only be decrypted once on a device, so if you - * try to decrypt a message that has been decrypted earlier in time, the decryption will fail. You should handle - * message history locally when using OMEMO, since you cannot rely on MAM. - * - * @param managerGuard omemoManager of the decrypting device. - * @param mamQueryResult mamQueryResult that shall be decrypted. - * @return list of decrypted messages. - * @throws InterruptedException - * @throws XMPPException.XMPPErrorException - * @throws SmackException.NotConnectedException - * @throws SmackException.NoResponseException - */ - List decryptMamQueryResult(OmemoManager.LoggedInOmemoManager managerGuard, - MamManager.MamQueryResult mamQueryResult) - throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, - SmackException.NoResponseException - { - List result = new ArrayList<>(); - for (Forwarded f : mamQueryResult.forwardedMessages) { - if (OmemoManager.stanzaContainsOmemoElement(f.getForwardedStanza())) { - // Decrypt OMEMO messages - try { - result.add(processLocalMessage(managerGuard, f.getForwardedStanza().getFrom().asBareJid(), - (Message) f.getForwardedStanza())); - } catch (NoRawSessionException | CorruptedOmemoKeyException | CryptoFailedException e) { - LOGGER.log(Level.WARNING, "decryptMamQueryResult failed to decrypt message from " - + f.getForwardedStanza().getFrom() + " due to corrupted session/key: " + e.getMessage()); - } - } else { - // Wrap cleartext messages - Message m = (Message) f.getForwardedStanza(); - result.add(new ClearTextMessage(m.getBody(), m, - new OmemoMessageInformation(null, null, - OmemoMessageInformation.CARBON.NONE, false))); - } - } - return result; - } - - /** - * Return the barejid of the user that sent the message inside the MUC. If the message wasn't sent in a MUC, - * return null; - * - * @param managerGuard omemoManager - * @param stanza message - * @return BareJid of the sender. - */ - private static OmemoDevice getSender(OmemoManager.LoggedInOmemoManager managerGuard, - Stanza stanza) { - OmemoElement omemoElement = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OMEMO_NAMESPACE_V_AXOLOTL); - Jid sender = stanza.getFrom(); - if (isMucMessage(managerGuard, stanza)) { - MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(managerGuard.get().getConnection()); - MultiUserChat muc = mucm.getMultiUserChat(sender.asEntityBareJidIfPossible()); - sender = muc.getOccupant(sender.asEntityFullJidIfPossible()).getJid().asBareJid(); - } - if (sender == null) { - throw new AssertionError("Sender is null."); - } - return new OmemoDevice(sender.asBareJid(), omemoElement.getHeader().getSid()); - } - - /** - * Return true, if the user knows a multiUserChat with a jid matching the sender of the stanza. - * @param managerGuard omemoManager of the user - * @param stanza stanza in question - * @return true if MUC message, otherwise false. - */ - private static boolean isMucMessage(OmemoManager.LoggedInOmemoManager managerGuard, Stanza stanza) { - BareJid sender = stanza.getFrom().asBareJid(); - MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(managerGuard.get().getConnection()); - - return mucm.getJoinedRooms().contains(sender.asEntityBareJidIfPossible()); - } - - @Override - public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) { - OmemoManager omemoManager = managerGuard.get(); - Message decrypted; - OmemoElement omemoMessage = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OMEMO_NAMESPACE_V_AXOLOTL); - OmemoMessageInformation messageInfo = new OmemoMessageInformation(); - MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(omemoManager.getConnection()); - OmemoDevice senderDevice = getSender(managerGuard, stanza); - try { - // Is it a MUC message... - if (isMucMessage(managerGuard, stanza)) { - - MultiUserChat muc = mucm.getMultiUserChat(stanza.getFrom().asEntityBareJidIfPossible()); - if (omemoMessage.isMessageElement()) { - - decrypted = processReceivingMessage(managerGuard, senderDevice, omemoMessage, messageInfo); - if (decrypted != null) { - omemoManager.notifyOmemoMucMessageReceived(muc, senderDevice.getJid(), decrypted.getBody(), - (Message) stanza, null, messageInfo); - } - - } else if (omemoMessage.isKeyTransportElement()) { - - CipherAndAuthTag cipherAndAuthTag = decryptTransportedOmemoKey(managerGuard, senderDevice, - omemoMessage, messageInfo); - if (cipherAndAuthTag != null) { - omemoManager.notifyOmemoMucKeyTransportMessageReceived(muc, senderDevice.getJid(), cipherAndAuthTag, - (Message) stanza, null, messageInfo); - } - } - } - // ... or a normal chat message... - else { - if (omemoMessage.isMessageElement()) { - - decrypted = processReceivingMessage(managerGuard, senderDevice, omemoMessage, messageInfo); - if (decrypted != null) { - omemoManager.notifyOmemoMessageReceived(decrypted.getBody(), (Message) stanza, null, - messageInfo); - } - - } else if (omemoMessage.isKeyTransportElement()) { - - CipherAndAuthTag cipherAndAuthTag = decryptTransportedOmemoKey(managerGuard, senderDevice, omemoMessage, - messageInfo); - if (cipherAndAuthTag != null) { - omemoManager.notifyOmemoKeyTransportMessageReceived(cipherAndAuthTag, (Message) stanza, - null, messageInfo); - } - } - } - - } catch (CryptoFailedException | CorruptedOmemoKeyException | InterruptedException | - SmackException.NotConnectedException | XMPPException.XMPPErrorException | SmackException.NoResponseException e) { - - LOGGER.log(Level.WARNING, "internal omemoMessageListener failed to decrypt incoming OMEMO message: " - + e.getMessage()); - - } catch (NoRawSessionException e) { - try { - LOGGER.log(Level.INFO, "Received message with invalid session from " + - senderDevice + ". Send RatchetUpdateMessage."); - sendOmemoRatchetUpdateMessage(managerGuard, senderDevice, true); - - } catch (UndecidedOmemoIdentityException | CorruptedOmemoKeyException | CannotEstablishOmemoSessionException - | CryptoFailedException e1) { - - LOGGER.log(Level.WARNING, "internal omemoMessageListener failed to establish a session for incoming OMEMO message: " - + e.getMessage()); - } - } - } - - @Override - public void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, - Message carbonCopy, - Message wrappingMessage, - final OmemoManager.LoggedInOmemoManager managerGuard) - { - OmemoManager omemoManager = managerGuard.get(); - - final OmemoDevice senderDevice = getSender(managerGuard, carbonCopy); - Message decrypted; - MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(omemoManager.getConnection()); - OmemoElement omemoMessage = carbonCopy.getExtension(OmemoElement.NAME_ENCRYPTED, OMEMO_NAMESPACE_V_AXOLOTL); - OmemoMessageInformation messageInfo = new OmemoMessageInformation(); - - if (CarbonExtension.Direction.received.equals(direction)) { - messageInfo.setCarbon(OmemoMessageInformation.CARBON.RECV); - } else { - messageInfo.setCarbon(OmemoMessageInformation.CARBON.SENT); - } - - try { - // Is it a MUC message... - if (isMucMessage(managerGuard, carbonCopy)) { - - MultiUserChat muc = mucm.getMultiUserChat(carbonCopy.getFrom().asEntityBareJidIfPossible()); - if (omemoMessage.isMessageElement()) { - - decrypted = processReceivingMessage(managerGuard, senderDevice, omemoMessage, messageInfo); - if (decrypted != null) { - omemoManager.notifyOmemoMucMessageReceived(muc, senderDevice.getJid(), decrypted.getBody(), - carbonCopy, wrappingMessage, messageInfo); - } - - } else if (omemoMessage.isKeyTransportElement()) { - - CipherAndAuthTag cipherAndAuthTag = decryptTransportedOmemoKey(managerGuard, senderDevice, - omemoMessage, messageInfo); - if (cipherAndAuthTag != null) { - omemoManager.notifyOmemoMucKeyTransportMessageReceived(muc, senderDevice.getJid(), cipherAndAuthTag, - carbonCopy, wrappingMessage, messageInfo); - } - } - } - // ... or a normal chat message... - else { - if (omemoMessage.isMessageElement()) { - - decrypted = processReceivingMessage(managerGuard, senderDevice, omemoMessage, messageInfo); - if (decrypted != null) { - omemoManager.notifyOmemoMessageReceived(decrypted.getBody(), carbonCopy, wrappingMessage, messageInfo); - } - - } else if (omemoMessage.isKeyTransportElement()) { - - CipherAndAuthTag cipherAndAuthTag = decryptTransportedOmemoKey(managerGuard, senderDevice, - omemoMessage, messageInfo); - if (cipherAndAuthTag != null) { - omemoManager.notifyOmemoKeyTransportMessageReceived(cipherAndAuthTag, carbonCopy, - null, messageInfo); - } - } - } - - } catch (CryptoFailedException | CorruptedOmemoKeyException | InterruptedException | - SmackException.NotConnectedException | XMPPException.XMPPErrorException | SmackException.NoResponseException e) { - LOGGER.log(Level.WARNING, "internal omemoMessageListener failed to decrypt incoming OMEMO carbon copy: " - + e.getMessage()); - - } catch (final NoRawSessionException e) { - Async.go(new Runnable() { - @Override - public void run() { - try { - LOGGER.log(Level.INFO, "Received OMEMO carbon copy message with invalid session from " + - senderDevice + ". Send RatchetUpdateMessage."); - sendOmemoRatchetUpdateMessage(managerGuard, senderDevice, true); - - } catch (UndecidedOmemoIdentityException | CorruptedOmemoKeyException | - CannotEstablishOmemoSessionException | CryptoFailedException e1) { - LOGGER.log(Level.WARNING, "internal omemoMessageListener failed to establish a session for incoming OMEMO carbon message: " - + e.getMessage()); - } - } - }); - - } - } - - protected abstract OmemoRatchet - instantiateOmemoRatchet(OmemoManager manager, - OmemoStore store); - - protected OmemoRatchet - getOmemoRatchet(OmemoManager manager) { - OmemoRatchet - omemoRatchet = omemoRatchets.get(manager); - if (omemoRatchet == null) { - omemoRatchet = instantiateOmemoRatchet(manager, omemoStore); - omemoRatchets.put(manager, omemoRatchet); - } - return omemoRatchet; - } -} - diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoStore.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoStore.java index 118a9ec8e..a0dee67ea 100644 --- a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoStore.java +++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/OmemoStore.java @@ -26,6 +26,7 @@ import java.util.TreeMap; import java.util.logging.Level; import java.util.logging.Logger; +import org.jivesoftware.smack.SmackException; import org.jivesoftware.smackx.omemo.element.OmemoBundleElement_VAxolotl; import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement; import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; @@ -559,22 +560,33 @@ public abstract class OmemoStore