Temp commit

This commit is contained in:
Paul Schaub 2017-12-31 16:30:27 +01:00
parent 4e5cf82795
commit 2db7717abc
45 changed files with 2587 additions and 1721 deletions

View File

@ -32,7 +32,7 @@ import org.jivesoftware.smack.roster.Roster;
import org.jivesoftware.smack.roster.RosterEntry; import org.jivesoftware.smack.roster.RosterEntry;
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
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.jivesoftware.smackx.omemo.util.OmemoConstants; import org.jivesoftware.smackx.omemo.util.OmemoConstants;
@ -185,7 +185,7 @@ public class OmemoManagerSetupHelper {
// ignore // ignore
} }
CachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend() OmemoCachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
.loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid()); .loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid());
for (int id : deviceList.getAllDevices()) { for (int id : deviceList.getAllDevices()) {

View File

@ -1,91 +0,0 @@
/**
*
* Copyright 2017 Paul Schaub
*
* This file is part of smack-omemo-signal.
*
* smack-omemo-signal is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
package org.jivesoftware.smack.omemo;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMODE;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
import static org.junit.Assert.assertArrayEquals;
import java.io.UnsupportedEncodingException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.junit.Test;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.IdentityKeyPair;
import org.whispersystems.libsignal.SessionCipher;
import org.whispersystems.libsignal.SignalProtocolAddress;
import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.state.PreKeyBundle;
import org.whispersystems.libsignal.state.PreKeyRecord;
import org.whispersystems.libsignal.state.SessionRecord;
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
/**
* Test the OmemoMessageBuilder.
*/
public class OmemoMessageBuilderTest extends SmackTestSuite {
@Test
public void setTextTest() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
Security.addProvider(new BouncyCastleProvider());
String message = "Hello World!";
byte[] key = OmemoMessageBuilder.generateKey();
byte[] iv = OmemoMessageBuilder.generateIv();
SecretKey secretKey = new SecretKeySpec(key, KEYTYPE);
IvParameterSpec ivSpec = new IvParameterSpec(iv);
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
OmemoMessageBuilder<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
mb = new OmemoMessageBuilder<>(null, null, key, iv);
mb.setMessage(message);
byte[] expected = cipher.doFinal(message.getBytes(StringUtils.UTF8));
byte[] messageKey = new byte[16];
System.arraycopy(mb.getMessageKey(),0, messageKey, 0, 16);
byte[] messagePlusTag = new byte[mb.getCiphertextMessage().length + 16];
System.arraycopy(mb.getCiphertextMessage(),0,messagePlusTag,0,mb.getCiphertextMessage().length);
System.arraycopy(mb.getMessageKey(), 16, messagePlusTag, mb.getCiphertextMessage().length, 16);
assertArrayEquals(key, messageKey);
assertArrayEquals(expected, messagePlusTag);
}
}

View File

@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNotNull;

View File

@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;

View File

@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
@ -31,7 +31,6 @@ import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException; import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException; import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException; import java.security.NoSuchProviderException;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;
import javax.crypto.IllegalBlockSizeException; import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException; import javax.crypto.NoSuchPaddingException;
@ -42,8 +41,6 @@ import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.test.util.SmackTestSuite; import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smack.test.util.TestUtils; import org.jivesoftware.smack.test.util.TestUtils;
import org.jivesoftware.smackx.omemo.OmemoManager;
import org.jivesoftware.smackx.omemo.element.OmemoElement; import org.jivesoftware.smackx.omemo.element.OmemoElement;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider; import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
@ -54,7 +51,7 @@ import org.junit.Test;
/** /**
* Test OmemoManager functionality. * Test OmemoManager functionality.
*/ */
public class OmemoManagerTest extends SmackTestSuite { public class SignalOmemoManagerTest extends SmackTestSuite {
@Test @Test
public void instantiationTest() public void instantiationTest()

View File

@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;

View File

@ -18,7 +18,7 @@
* along with this program; if not, write to the Free Software Foundation, * along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
@ -26,7 +26,6 @@ import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import org.jivesoftware.smackx.omemo.OmemoStore;
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore; import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore; import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil; import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;

View File

@ -23,7 +23,7 @@ import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice; import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil; import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
@ -345,8 +345,8 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
} }
@Override @Override
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) { public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
CachedDeviceList list = getCache(userDevice).deviceLists.get(contact); OmemoCachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
if (list == null && persistent != null) { if (list == null && persistent != null) {
list = persistent.loadCachedDeviceList(userDevice, contact); list = persistent.loadCachedDeviceList(userDevice, contact);
@ -355,14 +355,14 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
} }
} }
return list == null ? new CachedDeviceList() : new CachedDeviceList(list); return list == null ? new OmemoCachedDeviceList() : new OmemoCachedDeviceList(list);
} }
@Override @Override
public void storeCachedDeviceList(OmemoDevice userDevice, public void storeCachedDeviceList(OmemoDevice userDevice,
BareJid contact, BareJid contact,
CachedDeviceList deviceList) { OmemoCachedDeviceList deviceList) {
getCache(userDevice).deviceLists.put(contact, new CachedDeviceList(deviceList)); getCache(userDevice).deviceLists.put(contact, new OmemoCachedDeviceList(deviceList));
if (persistent != null) { if (persistent != null) {
persistent.storeCachedDeviceList(userDevice, contact, deviceList); persistent.storeCachedDeviceList(userDevice, contact, deviceList);
@ -417,7 +417,7 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
private final HashMap<BareJid, HashMap<Integer, T_Sess>> sessions = new HashMap<>(); private final HashMap<BareJid, HashMap<Integer, T_Sess>> sessions = new HashMap<>();
private final HashMap<OmemoDevice, T_IdKey> identityKeys = new HashMap<>(); private final HashMap<OmemoDevice, T_IdKey> identityKeys = new HashMap<>();
private final HashMap<OmemoDevice, Date> lastMessagesDates = new HashMap<>(); private final HashMap<OmemoDevice, Date> lastMessagesDates = new HashMap<>();
private final HashMap<BareJid, CachedDeviceList> deviceLists = new HashMap<>(); private final HashMap<BareJid, OmemoCachedDeviceList> deviceLists = new HashMap<>();
private Date lastRenewalDate = null; private Date lastRenewalDate = null;
} }
} }

View File

@ -36,7 +36,7 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice; import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
@ -345,8 +345,8 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
} }
@Override @Override
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) { public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
CachedDeviceList cachedDeviceList = new CachedDeviceList(); OmemoCachedDeviceList cachedDeviceList = new OmemoCachedDeviceList();
if (contact == null) { if (contact == null) {
throw new IllegalArgumentException("Contact can not be null."); throw new IllegalArgumentException("Contact can not be null.");
@ -372,7 +372,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
@Override @Override
public void storeCachedDeviceList(OmemoDevice userDevice, public void storeCachedDeviceList(OmemoDevice userDevice,
BareJid contact, BareJid contact,
CachedDeviceList contactsDeviceList) { OmemoCachedDeviceList contactsDeviceList) {
if (contact == null) { if (contact == null) {
return; return;
} }

View File

@ -54,11 +54,13 @@ public final class OmemoConfiguration {
/** /**
* Add Explicit Message Encryption hint (XEP-0380) to the message. * Add Explicit Message Encryption hint (XEP-0380) to the message.
* TODO: REMOVE
*/ */
private static boolean ADD_EME_ENCRYPTION_HINT = true; private static boolean ADD_EME_ENCRYPTION_HINT = true;
/** /**
* Add MAM storage hint to allow the server to store messages that do not contain a body. * Add MAM storage hint to allow the server to store messages that do not contain a body.
* TODO: REMOVE
*/ */
private static boolean ADD_MAM_STORAGE_HINT = true; private static boolean ADD_MAM_STORAGE_HINT = true;

View File

@ -16,7 +16,7 @@
*/ */
package org.jivesoftware.smackx.omemo; package org.jivesoftware.smackx.omemo;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.BODY_OMEMO_HINT;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY; import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY;
@ -26,11 +26,9 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Random; import java.util.Random;
import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.WeakHashMap; import java.util.WeakHashMap;
import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jivesoftware.smack.AbstractConnectionListener; import org.jivesoftware.smack.AbstractConnectionListener;
@ -40,7 +38,6 @@ import org.jivesoftware.smack.StanzaListener;
import org.jivesoftware.smack.XMPPConnection; import org.jivesoftware.smack.XMPPConnection;
import org.jivesoftware.smack.XMPPException; import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.filter.StanzaFilter; import org.jivesoftware.smack.filter.StanzaFilter;
import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.packet.Message; import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.packet.Stanza; import org.jivesoftware.smack.packet.Stanza;
@ -51,21 +48,18 @@ import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager; import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement; import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
import org.jivesoftware.smackx.hints.element.StoreHint; import org.jivesoftware.smackx.hints.element.StoreHint;
import org.jivesoftware.smackx.mam.MamManager;
import org.jivesoftware.smackx.muc.MultiUserChat; import org.jivesoftware.smackx.muc.MultiUserChat;
import org.jivesoftware.smackx.muc.MultiUserChatManager; import org.jivesoftware.smackx.muc.MultiUserChatManager;
import org.jivesoftware.smackx.muc.RoomInfo; import org.jivesoftware.smackx.muc.RoomInfo;
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl; import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
import org.jivesoftware.smackx.omemo.element.OmemoElement; import org.jivesoftware.smackx.omemo.element.OmemoElement;
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException; import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException;
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag; import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
import org.jivesoftware.smackx.omemo.internal.ClearTextMessage; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice; import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation; import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener; import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
@ -73,11 +67,7 @@ import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener;
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
import org.jivesoftware.smackx.omemo.trust.TrustCallback; import org.jivesoftware.smackx.omemo.trust.TrustCallback;
import org.jivesoftware.smackx.omemo.trust.TrustState; import org.jivesoftware.smackx.omemo.trust.TrustState;
import org.jivesoftware.smackx.pep.PEPListener;
import org.jivesoftware.smackx.pep.PEPManager; import org.jivesoftware.smackx.pep.PEPManager;
import org.jivesoftware.smackx.pubsub.EventElement;
import org.jivesoftware.smackx.pubsub.ItemsExtension;
import org.jivesoftware.smackx.pubsub.PayloadItem;
import org.jivesoftware.smackx.pubsub.PubSubException; import org.jivesoftware.smackx.pubsub.PubSubException;
import org.jivesoftware.smackx.pubsub.packet.PubSub; import org.jivesoftware.smackx.pubsub.packet.PubSub;
@ -134,7 +124,7 @@ public final class OmemoManager extends Manager {
}); });
} }
service.registerManager(this); service.registerRatchetForManager(this);
// StanzaListeners // StanzaListeners
startStanzaListeners(); startStanzaListeners();
@ -217,6 +207,14 @@ public final class OmemoManager extends Manager {
trustCallback = callback; trustCallback = callback;
} }
/**
* Return the TrustCallback of this manager.
* @return
*/
TrustCallback getTrustCallback() {
return trustCallback;
}
/** /**
* Initializes the OmemoManager. This method must be called before the manager can be used. * Initializes the OmemoManager. This method must be called before the manager can be used.
* *
@ -237,7 +235,7 @@ public final class OmemoManager extends Manager {
throw new SmackException.NotLoggedInException(); throw new SmackException.NotLoggedInException();
} }
getOmemoService().publish(new LoggedInOmemoManager(this)); getOmemoService().init(new LoggedInOmemoManager(this));
} }
} }
@ -261,11 +259,22 @@ public final class OmemoManager extends Manager {
}); });
} }
public List<OmemoDevice> getDevicesOf(BareJid recipient) {
OmemoCachedDeviceList list = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), recipient);
ArrayList<OmemoDevice> devices = new ArrayList<>();
for (int deviceId : list.getActiveDevices()) {
devices.add(new OmemoDevice(recipient, deviceId));
}
return devices;
}
/** /**
* OMEMO encrypt a cleartext message for a single recipient. * OMEMO encrypt a cleartext message for a single recipient.
* Note that this method does NOT set the 'to' attribute of the message. * Note that this method does NOT set the 'to' attribute of the message.
* *
* @param to recipients bareJid * @param recipient recipients bareJid
* @param message text to encrypt * @param message text to encrypt
* @return encrypted message * @return encrypted message
* @throws CryptoFailedException when something crypto related fails * @throws CryptoFailedException when something crypto related fails
@ -277,17 +286,15 @@ public final class OmemoManager extends Manager {
* @throws SmackException.NotConnectedException * @throws SmackException.NotConnectedException
* @throws SmackException.NoResponseException * @throws SmackException.NoResponseException
*/ */
public Message encrypt(BareJid to, String message) public OmemoMessage.Sent encrypt(BareJid recipient, String message)
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException, throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
SmackException.NoResponseException, SmackException.NotLoggedInException SmackException.NoResponseException, SmackException.NotLoggedInException
{ {
synchronized (LOCK) { synchronized (LOCK) {
LoggedInOmemoManager guard = new LoggedInOmemoManager(this); ArrayList<BareJid> recipients = new ArrayList<>();
Message plaintext = new Message(); recipients.add(recipient);
plaintext.setBody(message); return encrypt(recipients, message);
OmemoElement encrypted = getOmemoService().processSendingMessage(guard, to, plaintext);
return finishMessage(encrypted);
} }
} }
@ -306,17 +313,18 @@ public final class OmemoManager extends Manager {
* @throws SmackException.NotConnectedException * @throws SmackException.NotConnectedException
* @throws SmackException.NoResponseException * @throws SmackException.NoResponseException
*/ */
public Message encrypt(ArrayList<BareJid> recipients, String message) public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException, throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException, InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
SmackException.NoResponseException, SmackException.NotLoggedInException SmackException.NoResponseException, SmackException.NotLoggedInException
{ {
synchronized (LOCK) { synchronized (LOCK) {
Message m = new Message(); LoggedInOmemoManager guard = new LoggedInOmemoManager(this);
m.setBody(message); List<OmemoDevice> devices = getDevicesOf(getOwnJid());
OmemoElement encrypted = getOmemoService().processSendingMessage( for (BareJid recipient : recipients) {
new LoggedInOmemoManager(this), recipients, m); devices.addAll(getDevicesOf(recipient));
return finishMessage(encrypted); }
return service.createOmemoMessage(guard, devices, message);
} }
} }
@ -337,7 +345,7 @@ public final class OmemoManager extends Manager {
* @throws CannotEstablishOmemoSessionException when there is a user for whom we could not create a session * @throws CannotEstablishOmemoSessionException when there is a user for whom we could not create a session
* with any of their devices. * with any of their devices.
*/ */
public Message encrypt(MultiUserChat muc, String message) public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException, throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException, SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
@ -348,8 +356,6 @@ public final class OmemoManager extends Manager {
throw new NoOmemoSupportException(); throw new NoOmemoSupportException();
} }
Message m = new Message();
m.setBody(message);
ArrayList<BareJid> recipients = new ArrayList<>(); ArrayList<BareJid> recipients = new ArrayList<>();
for (EntityFullJid e : muc.getOccupants()) { for (EntityFullJid e : muc.getOccupants()) {
@ -359,75 +365,6 @@ public final class OmemoManager extends Manager {
} }
} }
/**
* Encrypt a message for all users we could build a session with successfully in a previous attempt.
* This method can come in handy as a fallback when encrypting a message fails due to devices we cannot
* build a session with.
*
* @param exception CannotEstablishSessionException from a previous encrypt(user(s), message) call.
* @param message message we want to send.
* @return encrypted message
* @throws CryptoFailedException
* @throws UndecidedOmemoIdentityException when there are undecided identities.
*/
public Message encryptForExistingSessions(CannotEstablishOmemoSessionException exception, String message)
throws CryptoFailedException, UndecidedOmemoIdentityException, SmackException.NotLoggedInException
{
synchronized (LOCK) {
Message m = new Message();
m.setBody(message);
OmemoElement encrypted = getOmemoService()
.encryptOmemoMessage(new LoggedInOmemoManager(this), exception.getSuccesses(), m);
return finishMessage(encrypted);
}
}
/**
* Decrypt an OMEMO message. This method comes handy when dealing with messages that were not automatically
* decrypted by smack-omemo, eg. MAM query messages.
* @param sender sender of the message
* @param omemoMessage message
* @return decrypted message
* @throws InterruptedException Exception
* @throws SmackException.NoResponseException Exception
* @throws SmackException.NotConnectedException Exception
* @throws CryptoFailedException When decryption fails
* @throws XMPPException.XMPPErrorException Exception
* @throws CorruptedOmemoKeyException When the used keys are invalid
* @throws NoRawSessionException When there is no double ratchet session found for this message
*/
public ClearTextMessage decrypt(BareJid sender, Message omemoMessage)
throws InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException,
CryptoFailedException, XMPPException.XMPPErrorException, CorruptedOmemoKeyException, NoRawSessionException,
SmackException.NotLoggedInException
{
synchronized (LOCK) {
return getOmemoService().processLocalMessage(new LoggedInOmemoManager(this), sender, omemoMessage);
}
}
/**
* Return a list of all OMEMO messages that were found in the MAM query result, that could be successfully
* decrypted. Normal cleartext messages are also added to this list.
*
* @param mamQueryResult mamQueryResult
* @return list of decrypted OmemoMessages
* @throws InterruptedException Exception
* @throws XMPPException.XMPPErrorException Exception
* @throws SmackException.NotConnectedException Exception
* @throws SmackException.NoResponseException Exception
*/
public List<ClearTextMessage> decryptMamQueryResult(MamManager.MamQueryResult mamQueryResult)
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
SmackException.NoResponseException, SmackException.NotLoggedInException
{
synchronized (LOCK) {
List<ClearTextMessage> l = new ArrayList<>();
l.addAll(getOmemoService().decryptMamQueryResult(new LoggedInOmemoManager(this), mamQueryResult));
return l;
}
}
/** /**
* Trust that a fingerprint belongs to an OmemoDevice. * Trust that a fingerprint belongs to an OmemoDevice.
* The fingerprint must be the lowercase, hexadecimal fingerprint of the identityKey of the device and must * The fingerprint must be the lowercase, hexadecimal fingerprint of the identityKey of the device and must
@ -501,72 +438,30 @@ public final class OmemoManager extends Manager {
* @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient * @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient
*/ */
public void sendRatchetUpdateMessage(OmemoDevice recipient) public void sendRatchetUpdateMessage(OmemoDevice recipient)
throws CorruptedOmemoKeyException, UndecidedOmemoIdentityException, CryptoFailedException, throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException,
CannotEstablishOmemoSessionException, SmackException.NotLoggedInException SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException,
CryptoFailedException, CannotEstablishOmemoSessionException
{ {
synchronized (LOCK) { synchronized (LOCK) {
getOmemoService().sendOmemoRatchetUpdateMessage( Message message = new Message();
new LoggedInOmemoManager(this), recipient, false); message.setFrom(getOwnJid());
message.setTo(recipient.getJid());
OmemoElement element = getOmemoService()
.createRatchetUpdateElement(new LoggedInOmemoManager(this), recipient);
message.addExtension(element);
// Set MAM Storage hint
StoreHint.set(message);
if (OmemoConfiguration.getAddEmeEncryptionHint()) {
message.addExtension(new ExplicitMessageEncryptionElement(
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
}
connection().sendStanza(message);
} }
} }
/**
* Create a new KeyTransportElement. This message will contain the AES-Key and IV that can be used eg. for encrypted
* Jingle file transfer.
*
* @param aesKey AES key to transport
* @param iv Initialization vector
* @param to list of recipient devices
* @return KeyTransportMessage
* @throws UndecidedOmemoIdentityException When the trust of session with the recipient is not decided yet
* @throws CorruptedOmemoKeyException When the used identityKeys are corrupted
* @throws CryptoFailedException When something fails with the crypto
* @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient
*/
public OmemoElement createKeyTransportElement(byte[] aesKey, byte[] iv, OmemoDevice ... to)
throws UndecidedOmemoIdentityException, CorruptedOmemoKeyException, CryptoFailedException,
CannotEstablishOmemoSessionException, SmackException.NotLoggedInException
{
synchronized (LOCK) {
return getOmemoService().prepareOmemoKeyTransportElement(
new LoggedInOmemoManager(this), aesKey, iv, to);
}
}
/**
* Create a new Message from a encrypted OmemoMessageElement.
* Add ourselves as the sender and the encrypted element.
* Also tell the server to store the message despite a possible missing body.
* The body will be set to a hint message that we are using OMEMO.
*
* @param encrypted OmemoMessageElement
* @return Message containing the OMEMO element and some additional information
*/
Message finishMessage(OmemoElement encrypted) {
if (encrypted == null) {
return null;
}
Message chatMessage = new Message();
chatMessage.setFrom(connection().getUser().asBareJid());
chatMessage.addExtension(encrypted);
if (OmemoConfiguration.getAddOmemoHintBody()) {
chatMessage.setBody(BODY_OMEMO_HINT);
}
if (OmemoConfiguration.getAddMAMStorageProcessingHint()) {
StoreHint.set(chatMessage);
}
if (OmemoConfiguration.getAddEmeEncryptionHint()) {
chatMessage.addExtension(new ExplicitMessageEncryptionElement(
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
}
return chatMessage;
}
/** /**
* Returns true, if the contact has any active devices published in a deviceList. * Returns true, if the contact has any active devices published in a deviceList.
* *
@ -577,15 +472,12 @@ public final class OmemoManager extends Manager {
* @throws SmackException.NoResponseException * @throws SmackException.NoResponseException
*/ */
public boolean contactSupportsOmemo(BareJid contact) public boolean contactSupportsOmemo(BareJid contact)
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
SmackException.NotLoggedInException SmackException.NotConnectedException, SmackException.NoResponseException
{ {
synchronized (LOCK) { synchronized (LOCK) {
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this); OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact);
return !deviceList.getActiveDevices().isEmpty();
getOmemoService().refreshDeviceList(managerGuard, contact);
return !getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact)
.getActiveDevices().isEmpty();
} }
} }
@ -657,7 +549,7 @@ public final class OmemoManager extends Manager {
return getOwnFingerprint(); return getOwnFingerprint();
} }
return getOmemoService().getOmemoStoreBackend().getFingerprint(new LoggedInOmemoManager(this), device); return getOmemoService().getOmemoStoreBackend().getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device);
} }
} }
@ -676,7 +568,7 @@ public final class OmemoManager extends Manager {
} }
HashMap<OmemoDevice, OmemoFingerprint> fingerprints = new HashMap<>(); HashMap<OmemoDevice, OmemoFingerprint> fingerprints = new HashMap<>();
CachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend() OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend()
.loadCachedDeviceList(getOwnDevice(), contact); .loadCachedDeviceList(getOwnDevice(), contact);
for (int id : deviceList.getActiveDevices()) { for (int id : deviceList.getActiveDevices()) {
@ -708,45 +600,28 @@ public final class OmemoManager extends Manager {
omemoMucMessageListeners.remove(listener); omemoMucMessageListeners.remove(listener);
} }
/**
* Build OMEMO sessions with devices of contact.
*
* @param contact contact we want to build session with.
* @throws InterruptedException
* @throws CannotEstablishOmemoSessionException
* @throws SmackException.NotConnectedException
* @throws SmackException.NoResponseException
*/
public void buildSessionsWith(BareJid contact)
throws InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
SmackException.NoResponseException, SmackException.NotLoggedInException
{
synchronized (LOCK) {
getOmemoService().buildMissingOmemoSessions(new LoggedInOmemoManager(this), contact);
}
}
/** /**
* Request a deviceList update from contact contact. * Request a deviceList update from contact contact.
* *
* @param contact contact we want to obtain the deviceList from. * @param contact contact we want to obtain the deviceList from.
* @throws SmackException.NotConnectedException
* @throws InterruptedException * @throws InterruptedException
* @throws PubSubException.NotALeafNodeException
* @throws XMPPException.XMPPErrorException
* @throws SmackException.NotConnectedException
* @throws SmackException.NoResponseException * @throws SmackException.NoResponseException
*/ */
public void requestDeviceListUpdateFor(BareJid contact) public void requestDeviceListUpdateFor(BareJid contact)
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException, throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
SmackException.NotLoggedInException SmackException.NotConnectedException, SmackException.NoResponseException {
{
synchronized (LOCK) { synchronized (LOCK) {
getOmemoService().refreshDeviceList(new LoggedInOmemoManager(this), contact); getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact);
} }
} }
/** /**
* Rotate the signedPreKey published in our OmemoBundle. This should be done every now and then (7-14 days). * Rotate the signedPreKey published in our OmemoBundle and republish it. This should be done every now and
* The old signedPreKey should be kept for some more time (a month or so) to enable decryption of messages * then (7-14 days). The old signedPreKey should be kept for some more time (a month or so) to enable decryption
* that have been sent since the key was changed. * of messages that have been sent since the key was changed.
* *
* @throws CorruptedOmemoKeyException When the IdentityKeyPair is damaged. * @throws CorruptedOmemoKeyException When the IdentityKeyPair is damaged.
* @throws InterruptedException XMPP error * @throws InterruptedException XMPP error
@ -756,16 +631,20 @@ public final class OmemoManager extends Manager {
* @throws PubSubException.NotALeafNodeException if the bundle node on the server is a CollectionNode * @throws PubSubException.NotALeafNodeException if the bundle node on the server is a CollectionNode
*/ */
public void rotateSignedPreKey() public void rotateSignedPreKey()
throws CorruptedOmemoKeyException, InterruptedException, XMPPException.XMPPErrorException, throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
SmackException.NotConnectedException, SmackException.NoResponseException, SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException
PubSubException.NotALeafNodeException, SmackException.NotLoggedInException
{ {
synchronized (LOCK) { synchronized (LOCK) {
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this); if (!connection().isAuthenticated()) {
throw new SmackException.NotLoggedInException();
}
// generate key // generate key
getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice()); getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice());
// publish // publish
getOmemoService().publish(managerGuard); OmemoBundleElement bundle = getOmemoService().getOmemoStoreBackend().packOmemoBundle(getOwnDevice());
OmemoService.publishBundle(connection(), getOwnDevice(), bundle);
} }
} }
@ -920,10 +799,10 @@ public final class OmemoManager extends Manager {
// Remove listeners to avoid them getting added twice // Remove listeners to avoid them getting added twice
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
pepManager.removePEPListener(deviceListUpdateListener); //pepManager.removePEPListener(deviceListUpdateListener);
// Add listeners // Add listeners
pepManager.addPEPListener(deviceListUpdateListener); //pepManager.addPEPListener(deviceListUpdateListener);
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter); connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
} }
@ -932,7 +811,7 @@ public final class OmemoManager extends Manager {
* Remove active stanza listeners needed for OMEMO. * Remove active stanza listeners needed for OMEMO.
*/ */
public void stopListeners() { public void stopListeners() {
PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener); //PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener); connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener); CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
} }
@ -956,90 +835,6 @@ public final class OmemoManager extends Manager {
return service; return service;
} }
private final PEPListener deviceListUpdateListener = new PEPListener() {
LoggedInOmemoManager managerGuard = null;
@Override
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
if (managerGuard == null) {
try {
managerGuard = new LoggedInOmemoManager(OmemoManager.this);
} catch (SmackException.NotLoggedInException e) {
throw new AssertionError(e);
}
}
for (ExtensionElement items : event.getExtensions()) {
if (!(items instanceof ItemsExtension)) {
continue;
}
for (NamedElement item : ((ItemsExtension) items).getItems()) {
if (!(item instanceof PayloadItem<?>)) {
continue;
}
PayloadItem<?> payloadItem = (PayloadItem<?>) item;
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement_VAxolotl)) {
continue;
}
// Device List <list>
OmemoDeviceListElement_VAxolotl omemoDeviceListElement =
(OmemoDeviceListElement_VAxolotl) payloadItem.getPayload();
Integer ourDeviceId = getDeviceId();
getOmemoService().getOmemoStoreBackend()
.mergeCachedDeviceList(managerGuard.get().getOwnDevice(), from, omemoDeviceListElement);
if (from == null) {
// Unknown sender, no more work to do.
// TODO: This DOES happen for some reason. Figure out when...
continue;
}
if (!from.equals(getOwnJid())) {
// Not our deviceList, so nothing more to do
continue;
}
if (omemoDeviceListElement.getDeviceIds().contains(ourDeviceId)) {
// We are on the list. Nothing more to do
continue;
}
// Our deviceList and we are not on it! We don't want to miss all the action!!!
LOGGER.log(Level.INFO, "Our deviceId was not on the list!");
Set<Integer> deviceListIds = omemoDeviceListElement.copyDeviceIds();
// enroll at the deviceList
deviceListIds.add(ourDeviceId);
final OmemoDeviceListElement_VAxolotl newOmemoDeviceListElement =
new OmemoDeviceListElement_VAxolotl(deviceListIds);
// PEPListener is a synchronous listener.
// Avoid any deadlocks by using an async task to update the device list.
Async.go(new Runnable() {
@Override
public void run() {
try {
OmemoService.publishDeviceIds(managerGuard, newOmemoDeviceListElement);
}
catch (SmackException | InterruptedException | XMPPException.XMPPErrorException e) {
// TODO: It might be dangerous NOT to retry publishing our deviceId
LOGGER.log(Level.SEVERE, "Could not publish our device list after an update " +
"without our id was received: " + e.getMessage());
}
}
});
}
}
}
};
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() { private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
@Override @Override
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException { public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {

View File

@ -0,0 +1,128 @@
/**
*
* Copyright 2017 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.omemo;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.BODY_OMEMO_HINT;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO;
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.jivesoftware.smack.packet.Message;
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
import org.jivesoftware.smackx.hints.element.StoreHint;
import org.jivesoftware.smackx.omemo.element.OmemoElement;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
public class OmemoMessage {
private final OmemoElement element;
OmemoMessage(OmemoElement element) {
this.element = element;
}
public OmemoElement getElement() {
return element;
}
public static class Sent extends OmemoMessage {
private final ArrayList<OmemoDevice> intendedDevices = new ArrayList<>();
private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
Sent(OmemoElement element, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
super(element);
this.intendedDevices.addAll(intendedDevices);
this.skippedDevices.putAll(skippedDevices);
}
public ArrayList<OmemoDevice> getIntendedDevices() {
return intendedDevices;
}
public HashMap<OmemoDevice, Throwable> getSkippedDevices() {
return skippedDevices;
}
public boolean isMissingRecipients() {
return !getSkippedDevices().isEmpty();
}
public Message asMessage() {
Message messageStanza = new Message();
messageStanza.addExtension(getElement());
if (OmemoConfiguration.getAddOmemoHintBody()) {
messageStanza.setBody(BODY_OMEMO_HINT);
}
StoreHint.set(messageStanza);
messageStanza.addExtension(new ExplicitMessageEncryptionElement(OMEMO_NAMESPACE_V_AXOLOTL, OMEMO));
return messageStanza;
}
}
public static class Received extends OmemoMessage {
private final String message;
private final OmemoFingerprint sendersFingerprint;
private final OmemoDevice senderDevice;
private final CARBON carbon;
Received(OmemoElement element, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon) {
super(element);
this.message = message;
this.sendersFingerprint = sendersFingerprint;
this.senderDevice = senderDevice;
this.carbon = carbon;
}
public String getMessage() {
return message;
}
public OmemoFingerprint getSendersFingerprint() {
return sendersFingerprint;
}
public OmemoDevice getSenderDevice() {
return senderDevice;
}
/**
* Return the carbon type.
*
* @return carbon type
*/
public CARBON getCarbon() {
return carbon;
}
}
/**
* Types of Carbon Messages.
*/
public enum CARBON {
NONE, //No carbon
SENT, //Sent carbon
RECV //Received Carbon
}
}

View File

@ -68,10 +68,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
int keyId = omemoManager.getDeviceId(); int keyId = omemoManager.getDeviceId();
byte[] unpackedKey = null; byte[] unpackedKey = null;
List<CryptoFailedException> decryptExceptions = new ArrayList<>(); List<CryptoFailedException> decryptExceptions = new ArrayList<>();
// CHECKSTYLE: OFF
@SuppressWarnings("unchecked")
List<OmemoKeyElement> keys = element.getHeader().getKeys(); List<OmemoKeyElement> keys = element.getHeader().getKeys();
// CHECKSTYLE: ON
// Find key with our ID. // Find key with our ID.
for (OmemoKeyElement k : keys) { for (OmemoKeyElement k : keys) {

File diff suppressed because it is too large Load Diff

View File

@ -30,7 +30,8 @@ import org.jivesoftware.smackx.omemo.element.OmemoBundleElement_VAxolotl;
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement; import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException; import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
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.jivesoftware.smackx.omemo.util.OmemoKeyUtil; import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
@ -81,12 +82,12 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
// Lookup local cached device list // Lookup local cached device list
BareJid ownJid = userDevice.getJid(); BareJid ownJid = userDevice.getJid();
CachedDeviceList cachedDeviceList; OmemoCachedDeviceList cachedDeviceList;
cachedDeviceList = loadCachedDeviceList(userDevice, ownJid); cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);
if (cachedDeviceList == null) { if (cachedDeviceList == null) {
cachedDeviceList = new CachedDeviceList(); cachedDeviceList = new OmemoCachedDeviceList();
} }
// Does the list already contain that id? // Does the list already contain that id?
return !cachedDeviceList.contains(id); return !cachedDeviceList.contains(id);
@ -99,11 +100,11 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @param contact Contact we received the list from. * @param contact Contact we received the list from.
* @param list List we received. * @param list List we received.
*/ */
CachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) { OmemoCachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
CachedDeviceList cached = loadCachedDeviceList(userDevice, contact); OmemoCachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
if (cached == null) { if (cached == null) {
cached = new CachedDeviceList(); cached = new OmemoCachedDeviceList();
} }
if (list != null) { if (list != null) {
@ -166,8 +167,6 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
/** /**
* Pack a OmemoBundleElement containing our key material. * Pack a OmemoBundleElement containing our key material.
* If we used up n preKeys since we last published our bundle, generate n new preKeys and add them to the bundle.
* We should always publish PRE_KEY_COUNT_PER_BUNDLE keys.
* *
* @param userDevice our OmemoDevice. * @param userDevice our OmemoDevice.
* @return OmemoBundleElement * @return OmemoBundleElement
@ -176,8 +175,6 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice) OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice)
throws CorruptedOmemoKeyException throws CorruptedOmemoKeyException
{ {
createMissingKeys(userDevice);
int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice); int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice);
T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKeys(userDevice).get(currentSignedPreKeyId); T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKeys(userDevice).get(currentSignedPreKeyId);
@ -190,7 +187,12 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
); );
} }
private void createMissingKeys(OmemoDevice userDevice) /**
* Replenish our supply of keys. If we are missing any type of keys, generate them fresh.
* @param userDevice
* @throws CorruptedOmemoKeyException
*/
public void replenishKeys(OmemoDevice userDevice)
throws CorruptedOmemoKeyException throws CorruptedOmemoKeyException
{ {
T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice); T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice);
@ -483,14 +485,14 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @param contact contact we want to get the deviceList of * @param contact contact we want to get the deviceList of
* @return CachedDeviceList of the contact * @return CachedDeviceList of the contact
*/ */
public abstract CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact); public abstract OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact);
/** /**
* Load a list of deviceIds from our own devices. * Load a list of deviceIds from our own devices.
* @param userDevice * @param userDevice
* @return * @return
*/ */
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) { public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) {
return loadCachedDeviceList(userDevice, userDevice.getJid()); return loadCachedDeviceList(userDevice, userDevice.getJid());
} }
@ -504,7 +506,7 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
*/ */
public abstract void storeCachedDeviceList(OmemoDevice userDevice, public abstract void storeCachedDeviceList(OmemoDevice userDevice,
BareJid contact, BareJid contact,
CachedDeviceList contactsDeviceList); OmemoCachedDeviceList contactsDeviceList);
/** /**
* Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions. * Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions.
@ -537,6 +539,24 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair)); return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair));
} }
/**
* Return the fingerprint of the identityKey belonging to contactsDevice.
*
* @param userDevice our OmemoDevice.
* @param contactsDevice OmemoDevice we want to have the fingerprint for.
* @return fingerprint of the userDevices IdentityKey.
* @throws CorruptedOmemoKeyException if the IdentityKey is corrupted.
* @throws NoIdentityKeyException if no IdentityKey for contactsDevice has been found locally.
*/
public OmemoFingerprint getFingerprint(OmemoDevice userDevice, OmemoDevice contactsDevice)
throws CorruptedOmemoKeyException, NoIdentityKeyException {
T_IdKey identityKey = loadOmemoIdentityKey(userDevice, contactsDevice);
if (identityKey == null) {
throw new NoIdentityKeyException(contactsDevice);
}
return keyUtil().getFingerprintOfIdentityKey(identityKey);
}
/** /**
* Return the fingerprint of the given devices announced identityKey. * Return the fingerprint of the given devices announced identityKey.
* *
@ -545,7 +565,7 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws CannotEstablishOmemoSessionException if we cannot establish a session * @throws CannotEstablishOmemoSessionException if we cannot establish a session
* @return fingerprint of the identityKey * @return fingerprint of the identityKey
*/ */
public OmemoFingerprint getFingerprint(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice) public OmemoFingerprint getFingerprintAndMaybeBuildSession(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException
{ {
OmemoManager omemoManager = managerGuard.get(); OmemoManager omemoManager = managerGuard.get();

View File

@ -23,6 +23,7 @@ import java.util.Set;
import org.jivesoftware.smack.packet.ExtensionElement; import org.jivesoftware.smack.packet.ExtensionElement;
import org.jivesoftware.smack.util.Objects; import org.jivesoftware.smack.util.Objects;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
/** /**
* A OMEMO device list update containing the IDs of all active devices of a contact. * A OMEMO device list update containing the IDs of all active devices of a contact.
@ -45,6 +46,10 @@ public abstract class OmemoDeviceListElement implements ExtensionElement {
this.deviceIds = Collections.unmodifiableSet(deviceIds); this.deviceIds = Collections.unmodifiableSet(deviceIds);
} }
public OmemoDeviceListElement(OmemoCachedDeviceList cachedList) {
this.deviceIds = Collections.unmodifiableSet(cachedList.getActiveDevices());
}
public Set<Integer> getDeviceIds() { public Set<Integer> getDeviceIds() {
return deviceIds; return deviceIds;
} }

View File

@ -20,6 +20,8 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_
import java.util.Set; import java.util.Set;
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
/** /**
* The OMEMO device list element with the legacy Axolotl namespace. * The OMEMO device list element with the legacy Axolotl namespace.
* *
@ -31,6 +33,10 @@ public class OmemoDeviceListElement_VAxolotl extends OmemoDeviceListElement {
super(deviceIds); super(deviceIds);
} }
public OmemoDeviceListElement_VAxolotl(OmemoCachedDeviceList cachedList) {
super(cachedList);
}
@Override @Override
public String getNamespace() { public String getNamespace() {
return OMEMO_NAMESPACE_V_AXOLOTL; return OMEMO_NAMESPACE_V_AXOLOTL;

View File

@ -17,6 +17,7 @@
package org.jivesoftware.smackx.omemo.element; package org.jivesoftware.smackx.omemo.element;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List;
import org.jivesoftware.smack.packet.NamedElement; import org.jivesoftware.smack.packet.NamedElement;
import org.jivesoftware.smack.util.XmlStringBuilder; import org.jivesoftware.smack.util.XmlStringBuilder;
@ -33,10 +34,10 @@ public abstract class OmemoHeaderElement implements NamedElement {
public static final String ATTR_IV = "iv"; public static final String ATTR_IV = "iv";
private final int sid; private final int sid;
private final ArrayList<OmemoKeyElement> keys; private final List<OmemoKeyElement> keys;
private final byte[] iv; private final byte[] iv;
public OmemoHeaderElement(int sid, ArrayList<OmemoKeyElement> keys, byte[] iv) { public OmemoHeaderElement(int sid, List<OmemoKeyElement> keys, byte[] iv) {
this.sid = sid; this.sid = sid;
this.keys = keys; this.keys = keys;
this.iv = iv; this.iv = iv;
@ -79,4 +80,4 @@ public abstract class OmemoHeaderElement implements NamedElement {
} }
} }

View File

@ -16,11 +16,11 @@
*/ */
package org.jivesoftware.smackx.omemo.element; package org.jivesoftware.smackx.omemo.element;
import java.util.ArrayList; import java.util.List;
public class OmemoHeaderElement_VAxolotl extends OmemoHeaderElement { public class OmemoHeaderElement_VAxolotl extends OmemoHeaderElement {
public OmemoHeaderElement_VAxolotl(int sid, ArrayList<OmemoKeyElement> keys, byte[] iv) { public OmemoHeaderElement_VAxolotl(int sid, List<OmemoKeyElement> keys, byte[] iv) {
super(sid, keys, iv); super(sid, keys, iv);
} }

View File

@ -65,4 +65,4 @@ public class OmemoKeyElement implements NamedElement {
sb.closeElement(this); sb.closeElement(this);
return sb; return sb;
} }
} }

View File

@ -35,10 +35,6 @@ public class CannotEstablishOmemoSessionException extends Exception {
private final HashMap<BareJid, HashMap<OmemoDevice, Throwable>> failures = new HashMap<>(); private final HashMap<BareJid, HashMap<OmemoDevice, Throwable>> failures = new HashMap<>();
private final HashMap<BareJid, ArrayList<OmemoDevice>> successes = new HashMap<>(); private final HashMap<BareJid, ArrayList<OmemoDevice>> successes = new HashMap<>();
public CannotEstablishOmemoSessionException() {
super();
}
public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) { public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) {
super(); super();
getFailsOfContact(failed.getJid()).put(failed, reason); getFailsOfContact(failed.getJid()).put(failed, reason);

View File

@ -16,6 +16,10 @@
*/ */
package org.jivesoftware.smackx.omemo.exceptions; package org.jivesoftware.smackx.omemo.exceptions;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/** /**
* Exception gets thrown when some cryptographic function failed. * Exception gets thrown when some cryptographic function failed.
* *
@ -25,11 +29,18 @@ public class CryptoFailedException extends Exception {
private static final long serialVersionUID = 3466888654338119924L; private static final long serialVersionUID = 3466888654338119924L;
private final ArrayList<Exception> exceptions = new ArrayList<>();
public CryptoFailedException(String message) { public CryptoFailedException(String message) {
super(message); super(message);
} }
public CryptoFailedException(Exception e) { public CryptoFailedException(Exception e) {
super(e); super(e);
exceptions.add(e);
}
public List<Exception> getExceptions() {
return Collections.unmodifiableList(exceptions);
} }
} }

View File

@ -16,25 +16,18 @@
*/ */
package org.jivesoftware.smackx.omemo.exceptions; package org.jivesoftware.smackx.omemo.exceptions;
import java.io.IOException; import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import java.util.ArrayList;
import java.util.Arrays;
public class MultipleIOException extends IOException {
public class NoIdentityKeyException extends Exception {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private final ArrayList<IOException> exceptions = new ArrayList<>(); private final OmemoDevice device;
public MultipleIOException(IOException... exceptions) { public NoIdentityKeyException(OmemoDevice device) {
this.exceptions.addAll(Arrays.asList(exceptions)); this.device = device;
} }
public void addException(IOException e) { public OmemoDevice getDevice() {
exceptions.add(e); return device;
}
public ArrayList<IOException> getExceptions() {
return exceptions;
} }
} }

View File

@ -16,6 +16,7 @@
*/ */
package org.jivesoftware.smackx.omemo.exceptions; package org.jivesoftware.smackx.omemo.exceptions;
import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice; import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
@ -34,6 +35,11 @@ public class UndecidedOmemoIdentityException extends Exception {
this.devices.add(contact); this.devices.add(contact);
} }
public UndecidedOmemoIdentityException(Collection<OmemoDevice> devices) {
super();
this.devices.addAll(devices);
}
/** /**
* Return the HashSet of undecided devices. * Return the HashSet of undecided devices.
* *

View File

@ -20,8 +20,9 @@ import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint; import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
/** /**
* Exception that gets thrown when we try to decrypt a message which contains an identityKey that differs from the one * Exception that gets thrown when we try to en-/decrypt a message for an untrusted contact.
* we previously trusted. * This might either be because the user actively untrusted a device, or we receive a message from a contact
* which contains an identityKey that differs from the one the user trusted.
*/ */
public class UntrustedOmemoIdentityException extends Exception { public class UntrustedOmemoIdentityException extends Exception {
@ -30,7 +31,8 @@ public class UntrustedOmemoIdentityException extends Exception {
private final OmemoFingerprint trustedKey, untrustedKey; private final OmemoFingerprint trustedKey, untrustedKey;
/** /**
* Constructor. * Constructor for when we receive a message with an identityKey different from the one we trusted.
*
* @param device device which sent the message. * @param device device which sent the message.
* @param fpTrusted fingerprint of the identityKey we previously had and trusted. * @param fpTrusted fingerprint of the identityKey we previously had and trusted.
* @param fpUntrusted fingerprint of the new key which is untrusted. * @param fpUntrusted fingerprint of the new key which is untrusted.
@ -42,6 +44,16 @@ public class UntrustedOmemoIdentityException extends Exception {
this.untrustedKey = fpUntrusted; this.untrustedKey = fpUntrusted;
} }
/**
* Constructor for when encryption fails because the user untrusted a recipients device.
*
* @param device device the user wants to encrypt for, but which has been marked as untrusted.
* @param untrustedKey fingerprint of that device.
*/
public UntrustedOmemoIdentityException(OmemoDevice device, OmemoFingerprint untrustedKey) {
this(device, null, untrustedKey);
}
/** /**
* Return the device which sent the message. * Return the device which sent the message.
* @return omemoDevice. * @return omemoDevice.
@ -52,6 +64,7 @@ public class UntrustedOmemoIdentityException extends Exception {
/** /**
* Return the fingerprint of the key we expected. * Return the fingerprint of the key we expected.
* This might return null in case this exception got thrown during encryption process.
* @return * @return
*/ */
public OmemoFingerprint getTrustedFingerprint() { public OmemoFingerprint getTrustedFingerprint() {

View File

@ -1,63 +0,0 @@
/**
*
* Copyright 2017 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.omemo.internal;
import org.jivesoftware.smack.packet.Message;
/**
* Class that bundles a decrypted message together with the original message and some information about the encryption.
*
* @author Paul Schaub
*/
public class ClearTextMessage {
private final String body;
private final Message encryptedMessage;
private final OmemoMessageInformation messageInformation;
public ClearTextMessage(String message, Message original, OmemoMessageInformation messageInfo) {
this.body = message;
this.encryptedMessage = original;
this.messageInformation = messageInfo;
}
/**
* Return the body of the decrypted message.
*
* @return plaintext body
*/
public String getBody() {
return body;
}
/**
* Return the original Message object.
*
* @return original message
*/
public Message getOriginalMessage() {
return encryptedMessage;
}
/**
* Return the OmemoMessageInformation.
*
* @return omemoMessageInformation
*/
public OmemoMessageInformation getMessageInformation() {
return messageInformation;
}
}

View File

@ -32,24 +32,24 @@ import java.util.Set;
* *
* @author Paul Schaub * @author Paul Schaub
*/ */
public class CachedDeviceList implements Serializable { public class OmemoCachedDeviceList implements Serializable {
private static final long serialVersionUID = 3153579238321261203L; private static final long serialVersionUID = 3153579238321261203L;
private final Set<Integer> activeDevices; private final Set<Integer> activeDevices;
private final Set<Integer> inactiveDevices; private final Set<Integer> inactiveDevices;
public CachedDeviceList() { public OmemoCachedDeviceList() {
this.activeDevices = new HashSet<>(); this.activeDevices = new HashSet<>();
this.inactiveDevices = new HashSet<>(); this.inactiveDevices = new HashSet<>();
} }
public CachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) { public OmemoCachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
this(); this();
this.activeDevices.addAll(activeDevices); this.activeDevices.addAll(activeDevices);
this.inactiveDevices.addAll(inactiveDevices); this.inactiveDevices.addAll(inactiveDevices);
} }
public CachedDeviceList(CachedDeviceList original) { public OmemoCachedDeviceList(OmemoCachedDeviceList original) {
this(original.getActiveDevices(), original.getInactiveDevices()); this(original.getActiveDevices(), original.getInactiveDevices());
} }
@ -109,6 +109,11 @@ public class CachedDeviceList implements Serializable {
inactiveDevices.remove(deviceId); inactiveDevices.remove(deviceId);
} }
public void addInactiveDevice(int deviceId) {
activeDevices.remove(deviceId);
inactiveDevices.add(deviceId);
}
/** /**
* Returns true if deviceId is either in the list of active or inactive devices. * Returns true if deviceId is either in the list of active or inactive devices.
* *

View File

@ -16,6 +16,8 @@
*/ */
package org.jivesoftware.smackx.omemo.internal; package org.jivesoftware.smackx.omemo.internal;
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
import org.jxmpp.jid.BareJid; import org.jxmpp.jid.BareJid;
/** /**
@ -74,4 +76,12 @@ public class OmemoDevice {
i = jid.hashCode() + deviceId; i = jid.hashCode() + deviceId;
return i.hashCode(); return i.hashCode();
} }
/**
* Return the name of the PubSub {@link org.jivesoftware.smackx.pubsub.LeafNode} of this device.
* @return node name.
*/
public String getBundleNodeName() {
return OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(getDeviceId());
}
} }

View File

@ -37,21 +37,21 @@ import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
import org.jivesoftware.smack.SmackException;
import org.jivesoftware.smack.util.StringUtils; import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.omemo.OmemoManager;
import org.jivesoftware.smackx.omemo.OmemoRatchet; import org.jivesoftware.smackx.omemo.OmemoRatchet;
import org.jivesoftware.smackx.omemo.OmemoService;
import org.jivesoftware.smackx.omemo.element.OmemoElement; import org.jivesoftware.smackx.omemo.element.OmemoElement;
import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl; import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl;
import org.jivesoftware.smackx.omemo.element.OmemoHeaderElement_VAxolotl; import org.jivesoftware.smackx.omemo.element.OmemoHeaderElement_VAxolotl;
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement; import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException; import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException; import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple; import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
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.jivesoftware.smackx.omemo.trust.TrustCallback;
/** /**
@ -69,22 +69,27 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
* @author Paul Schaub * @author Paul Schaub
*/ */
public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> { public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
private final OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet;
private final OmemoManager.LoggedInOmemoManager managerGuard;
private byte[] messageKey = generateKey(); private final OmemoDevice userDevice;
private byte[] initializationVector = generateIv(); private final OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet;
private final TrustCallback trustCallback;
private byte[] messageKey;
private final byte[] initializationVector;
private byte[] ciphertextMessage; private byte[] ciphertextMessage;
private final ArrayList<OmemoKeyElement> keys = new ArrayList<>(); private final ArrayList<OmemoKeyElement> keys = new ArrayList<>();
/** /**
* Create a OmemoMessageBuilder. * Create an OmemoMessageBuilder.
*
* @param userDevice our OmemoDevice
* @param callback trustCallback for querying trust decisions
* @param ratchet our OmemoRatchet
* @param aesKey aes message key used for message encryption
* @param iv initialization vector used for message encryption
* @param message message we want to send
* *
* @param managerGuard OmemoManager of our device.
* @param ratchet OmemoRatchet.
* @param aesKey AES key that will be transported to the recipient. This is used eg. to encrypt the body.
* @param iv IV
* @throws NoSuchPaddingException * @throws NoSuchPaddingException
* @throws BadPaddingException * @throws BadPaddingException
* @throws InvalidKeyException * @throws InvalidKeyException
@ -94,24 +99,31 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws NoSuchProviderException * @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException * @throws InvalidAlgorithmParameterException
*/ */
public OmemoMessageBuilder(OmemoManager.LoggedInOmemoManager managerGuard, public OmemoMessageBuilder(OmemoDevice userDevice,
TrustCallback callback,
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
byte[] aesKey, byte[] iv) byte[] aesKey,
byte[] iv,
String message)
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
IllegalBlockSizeException, IllegalBlockSizeException,
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException { UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
this.managerGuard = managerGuard; this.userDevice = userDevice;
this.trustCallback = callback;
this.ratchet = ratchet; this.ratchet = ratchet;
this.messageKey = aesKey; this.messageKey = aesKey;
this.initializationVector = iv; this.initializationVector = iv;
setMessage(message);
} }
/** /**
* Create a new OmemoMessageBuilder with random IV and AES key. * Create an OmemoMessageBuilder.
*
* @param userDevice our OmemoDevice
* @param callback trustCallback for querying trust decisions
* @param ratchet our OmemoRatchet
* @param message message we want to send
* *
* @param managerGuard omemoManager of our device.
* @param ratchet omemoSessionManager.
* @param message Messages body.
* @throws NoSuchPaddingException * @throws NoSuchPaddingException
* @throws BadPaddingException * @throws BadPaddingException
* @throws InvalidKeyException * @throws InvalidKeyException
@ -121,31 +133,32 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @throws NoSuchProviderException * @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException * @throws InvalidAlgorithmParameterException
*/ */
public OmemoMessageBuilder(OmemoManager.LoggedInOmemoManager managerGuard, public OmemoMessageBuilder(OmemoDevice userDevice,
TrustCallback callback,
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
String message) String message)
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException { UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
this.managerGuard = managerGuard; this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message);
this.ratchet = ratchet;
this.setMessage(message);
} }
/** /**
* Create an AES messageKey and use it to encrypt the message. * Encrypt the message with the aes key.
* Optionally append the Auth Tag of the encrypted message to the messageKey afterwards. * Move the AuthTag from the end of the cipherText to the end of the messageKey afterwards.
* This prevents an attacker which compromised one recipient device to switch out the cipherText for other recipients.
* @see <a href="https://conversations.im/omemo/audit.pdf">OMEMO security audit</a>.
* *
* @param message content of the message * @param message plaintext message
* @throws NoSuchPaddingException When no Cipher could be instantiated. * @throws NoSuchPaddingException
* @throws NoSuchAlgorithmException when no Cipher could be instantiated. * @throws NoSuchAlgorithmException
* @throws NoSuchProviderException when BouncyCastle could not be found. * @throws NoSuchProviderException
* @throws InvalidAlgorithmParameterException when the Cipher could not be initialized * @throws InvalidAlgorithmParameterException
* @throws InvalidKeyException when the generated key is invalid * @throws InvalidKeyException
* @throws UnsupportedEncodingException when UTF8 is unavailable * @throws UnsupportedEncodingException
* @throws BadPaddingException when cipher.doFinal gets wrong padding * @throws BadPaddingException
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size. * @throws IllegalBlockSizeException
*/ */
public void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException { private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
if (message == null) { if (message == null) {
return; return;
} }
@ -165,56 +178,70 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
byte[] clearKeyWithAuthTag = new byte[messageKey.length + 16]; byte[] clearKeyWithAuthTag = new byte[messageKey.length + 16];
byte[] cipherTextWithoutAuthTag = new byte[ciphertext.length - 16]; byte[] cipherTextWithoutAuthTag = new byte[ciphertext.length - 16];
System.arraycopy(messageKey, 0, clearKeyWithAuthTag, 0, 16); moveAuthTag(messageKey, ciphertext, clearKeyWithAuthTag, cipherTextWithoutAuthTag);
System.arraycopy(ciphertext, 0, cipherTextWithoutAuthTag, 0, cipherTextWithoutAuthTag.length);
System.arraycopy(ciphertext, ciphertext.length - 16, clearKeyWithAuthTag, 16, 16);
ciphertextMessage = cipherTextWithoutAuthTag; ciphertextMessage = cipherTextWithoutAuthTag;
messageKey = clearKeyWithAuthTag; messageKey = clearKeyWithAuthTag;
} }
/** /**
* Add a new recipient device to the message. * Move the auth tag from the end of the cipherText to the messageKey.
* *
* @param device recipient device * @param messageKey source messageKey without authTag
* @throws CryptoFailedException when encrypting the messageKey fails * @param cipherText source cipherText with authTag
* @throws UndecidedOmemoIdentityException * @param messageKeyWithAuthTag destination messageKey with authTag
* @throws CorruptedOmemoKeyException * @param cipherTextWithoutAuthTag destination cipherText without authTag
*/ */
public void addRecipient(OmemoDevice device) static void moveAuthTag(byte[] messageKey,
throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException, byte[] cipherText,
CannotEstablishOmemoSessionException { byte[] messageKeyWithAuthTag,
addRecipient(device, false); byte[] cipherTextWithoutAuthTag) {
// Check dimensions of arrays
if (messageKeyWithAuthTag.length != messageKey.length + 16) {
throw new IllegalArgumentException("Length of messageKeyWithAuthTag must be length of messageKey + " +
"length of AuthTag (16)");
}
if (cipherTextWithoutAuthTag.length != cipherText.length - 16) {
throw new IllegalArgumentException("Length of cipherTextWithoutAuthTag must be length of cipherText " +
"- length of AuthTag (16)");
}
// Move auth tag from cipherText to messageKey
System.arraycopy(messageKey, 0, messageKeyWithAuthTag, 0, 16);
System.arraycopy(cipherText, 0, cipherTextWithoutAuthTag, 0, cipherTextWithoutAuthTag.length);
System.arraycopy(cipherText, cipherText.length - 16, messageKeyWithAuthTag, 16, 16);
} }
/** /**
* Add a new recipient device to the message. * Add a new recipient device to the message.
* @param contactsDevice recipient device *
* @param ignoreTrust ignore current trust state? Useful for keyTransportMessages that are sent to repair a session * @param contactsDevice device of the recipient
* @throws CryptoFailedException * @throws NoIdentityKeyException if we have no identityKey of that device. Can be fixed by fetching and
* @throws UndecidedOmemoIdentityException * processing the devices bundle.
* @throws CorruptedOmemoKeyException * @throws CorruptedOmemoKeyException if the identityKey of that device is corrupted.
* @throws UndecidedOmemoIdentityException if the user hasn't yet decided whether to trust that device or not.
* @throws UntrustedOmemoIdentityException if the user has decided not to trust that device.
*/ */
public void addRecipient(OmemoDevice contactsDevice, boolean ignoreTrust) throws public void addRecipient(OmemoDevice contactsDevice)
CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException, throws NoIdentityKeyException, CorruptedOmemoKeyException, UndecidedOmemoIdentityException,
CannotEstablishOmemoSessionException { UntrustedOmemoIdentityException {
OmemoFingerprint fingerprint; OmemoFingerprint fingerprint;
try { fingerprint = OmemoService.getInstance().getOmemoStoreBackend().getFingerprint(userDevice, contactsDevice);
fingerprint = managerGuard.get().getFingerprint(contactsDevice);
} catch (SmackException.NotLoggedInException e) {
throw new AssertionError("This should never happen.");
}
if (!ignoreTrust && !managerGuard.get().isDecidedOmemoIdentity(contactsDevice, fingerprint)) { switch (trustCallback.getTrust(contactsDevice, fingerprint)) {
// Warn user of undecided device
throw new UndecidedOmemoIdentityException(contactsDevice); case undecided:
} throw new UndecidedOmemoIdentityException(contactsDevice);
case trusted:
CiphertextTuple encryptedKey = ratchet.doubleRatchetEncrypt(contactsDevice, messageKey);
keys.add(new OmemoKeyElement(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
case untrusted:
throw new UntrustedOmemoIdentityException(contactsDevice, fingerprint);
if (ignoreTrust || managerGuard.get().isTrustedOmemoIdentity(contactsDevice, fingerprint)) {
// Encrypt key and save to header
CiphertextTuple encryptedKey = ratchet.doubleRatchetEncrypt(contactsDevice, messageKey);
keys.add(new OmemoKeyElement(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
} }
} }
@ -225,7 +252,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
*/ */
public OmemoElement finish() { public OmemoElement finish() {
OmemoHeaderElement_VAxolotl header = new OmemoHeaderElement_VAxolotl( OmemoHeaderElement_VAxolotl header = new OmemoHeaderElement_VAxolotl(
managerGuard.get().getDeviceId(), userDevice.getDeviceId(),
keys, keys,
initializationVector initializationVector
); );
@ -238,9 +265,9 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
* @return new AES key * @return new AES key
* @throws NoSuchAlgorithmException * @throws NoSuchAlgorithmException
*/ */
public static byte[] generateKey() throws NoSuchAlgorithmException { public static byte[] generateKey(String keyType, int keyLength) throws NoSuchAlgorithmException {
KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE); KeyGenerator generator = KeyGenerator.getInstance(keyType);
generator.init(KEYLENGTH); generator.init(keyLength);
return generator.generateKey().getEncoded(); return generator.generateKey().getEncoded();
} }
@ -255,12 +282,4 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
random.nextBytes(iv); random.nextBytes(iv);
return iv; return iv;
} }
public byte[] getCiphertextMessage() {
return ciphertextMessage;
}
public byte[] getMessageKey() {
return messageKey;
}
} }

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNotNull;
@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
import org.junit.Test; import org.junit.Test;
@ -43,7 +43,7 @@ public class DeviceListTest {
*/ */
@Test @Test
public void mergeDeviceListsTest() { public void mergeDeviceListsTest() {
CachedDeviceList cached = new CachedDeviceList(); OmemoCachedDeviceList cached = new OmemoCachedDeviceList();
assertNotNull(cached.getActiveDevices()); assertNotNull(cached.getActiveDevices());
assertNotNull(cached.getInactiveDevices()); assertNotNull(cached.getInactiveDevices());

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.fail; import static junit.framework.TestCase.fail;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotSame; import static junit.framework.TestCase.assertNotSame;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertNotNull; import static junit.framework.TestCase.assertNotNull;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull; import static org.junit.Assert.assertNull;

View File

@ -0,0 +1,61 @@
/**
*
* Copyright 2017 Paul Schaub
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertFalse;
import static junit.framework.TestCase.assertTrue;
import java.util.Date;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
import org.junit.Test;
import org.jxmpp.jid.impl.JidCreate;
import org.jxmpp.stringprep.XmppStringprepException;
public class OmemoServiceTest extends SmackTestSuite {
private static final long ONE_HOUR = 1000L * 60 * 60;
private static final int IGNORE_STALE = OmemoConfiguration.getIgnoreStaleDevicesAfterHours();
private static final int DELETE_STALE = OmemoConfiguration.getDeleteStaleDevicesAfterHours();
/**
* Test correct functionality of isStale method.
* @throws XmppStringprepException
*/
@Test
public void isStaleDeviceTest() throws XmppStringprepException {
OmemoDevice user = new OmemoDevice(JidCreate.bareFrom("alice@wonderland.lit"), 123);
OmemoDevice other = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 444);
Date now = new Date();
Date ignoreMe = new Date(now.getTime() - ((IGNORE_STALE + 1) * ONE_HOUR));
Date deleteMe = new Date(now.getTime() - ((DELETE_STALE + 1) * ONE_HOUR));
Date imFine = new Date(now.getTime() - ONE_HOUR);
// One hour "old" devices are (probably) not not stale
assertFalse(OmemoService.isStale(user, other, imFine, IGNORE_STALE));
// Devices one hour "older" than max ages are stale
assertTrue(OmemoService.isStale(user, other, ignoreMe, IGNORE_STALE));
assertTrue(OmemoService.isStale(user, other, deleteMe, DELETE_STALE));
// Own device is never stale, no matter how old
assertFalse(OmemoService.isStale(user, user, deleteMe, DELETE_STALE));
}
}

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertFalse; import static junit.framework.TestCase.assertFalse;
@ -31,10 +31,8 @@ import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.TreeMap; import java.util.TreeMap;
import org.jivesoftware.smackx.omemo.FileBasedOmemoStore;
import org.jivesoftware.smackx.omemo.OmemoStore;
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException; import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList; import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
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;
@ -273,7 +271,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
public void loadStoreCachedDeviceList() throws IOException { public void loadStoreCachedDeviceList() throws IOException {
Integer[] active = new Integer[] {1,5,999,10}; Integer[] active = new Integer[] {1,5,999,10};
Integer[] inactive = new Integer[] {6,7,8}; Integer[] inactive = new Integer[] {6,7,8};
CachedDeviceList before = new CachedDeviceList( OmemoCachedDeviceList before = new OmemoCachedDeviceList(
new HashSet<>(Arrays.asList(active)), new HashSet<>(Arrays.asList(active)),
new HashSet<>(Arrays.asList(inactive))); new HashSet<>(Arrays.asList(inactive)));
@ -281,7 +279,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
store.loadCachedDeviceList(alice, bob.getJid())); store.loadCachedDeviceList(alice, bob.getJid()));
store.storeCachedDeviceList(alice, bob.getJid(), before); store.storeCachedDeviceList(alice, bob.getJid(), before);
CachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid()); OmemoCachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid());
assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0); assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0);
assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size()); assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size());
@ -298,7 +296,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
assertTrue(after.getAllDevices().contains(i)); assertTrue(after.getAllDevices().contains(i));
} }
store.storeCachedDeviceList(alice, bob.getJid(), new CachedDeviceList()); store.storeCachedDeviceList(alice, bob.getJid(), new OmemoCachedDeviceList());
assertEquals("DeviceList must be empty after overwriting it with empty list.", 0, assertEquals("DeviceList must be empty after overwriting it with empty list.", 0,
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size()); store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
} }

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo; package org.jivesoftware.smackx.omemo;
import static junit.framework.TestCase.assertEquals; import static junit.framework.TestCase.assertEquals;
import static junit.framework.TestCase.assertTrue; import static junit.framework.TestCase.assertTrue;

View File

@ -14,7 +14,7 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
package org.jivesoftware.smack.omemo.util; package org.jivesoftware.smackx.omemo.util;
import java.util.HashMap; import java.util.HashMap;

View File

@ -0,0 +1,79 @@
/**
*
* Copyright the original author or authors
*
* 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.util;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Random;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.junit.Test;
public class OmemoMessageBuilderTest extends SmackTestSuite {
private static final byte[] messageKey = new byte[16];
private static final byte[] cipherTextWithAuthTag = new byte[35 + 16];
public OmemoMessageBuilderTest() {
Random random = new Random();
random.nextBytes(messageKey);
random.nextBytes(cipherTextWithAuthTag);
}
@Test
public void testMoveAuthTag() {
// Extract authTag for testing purposes
byte[] authTag = new byte[16];
System.arraycopy(cipherTextWithAuthTag, 35, authTag, 0, 16);
byte[] messageKeyWithAuthTag = new byte[16 + 16];
byte[] cipherTextWithoutAuthTag = new byte[35];
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, messageKeyWithAuthTag, cipherTextWithoutAuthTag);
// Check if first n - 16 bytes of cipherText got copied over to cipherTextWithoutAuthTag correctly
byte[] checkCipherText = new byte[35];
System.arraycopy(cipherTextWithAuthTag, 0, checkCipherText, 0, 35);
assertTrue(Arrays.equals(checkCipherText, cipherTextWithoutAuthTag));
byte[] checkMessageKey = new byte[16];
System.arraycopy(messageKeyWithAuthTag, 0, checkMessageKey, 0, 16);
assertTrue(Arrays.equals(checkMessageKey, messageKey));
byte[] checkAuthTag = new byte[16];
System.arraycopy(messageKeyWithAuthTag, 16, checkAuthTag, 0, 16);
assertTrue(Arrays.equals(checkAuthTag, authTag));
}
@Test(expected = IllegalArgumentException.class)
public void testCheckIllegalMessageKeyWithAuthTagLength() {
byte[] illegalMessageKey = new byte[16 + 15]; // too short
byte[] cipherTextWithoutAuthTag = new byte[35]; // ok
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, illegalMessageKey, cipherTextWithoutAuthTag);
}
@Test(expected = IllegalArgumentException.class)
public void testCheckIllegalCipherTextWithoutAuthTagLength() {
byte[] messageKeyWithAuthTag = new byte[16 + 16]; // ok
byte[] illegalCipherTextWithoutAuthTag = new byte[39]; // too long
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, messageKeyWithAuthTag, illegalCipherTextWithoutAuthTag);
}
}