mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-05 03:35:59 +01:00
Temp commit
This commit is contained in:
parent
4e5cf82795
commit
2db7717abc
45 changed files with 2587 additions and 1721 deletions
|
@ -32,7 +32,7 @@ import org.jivesoftware.smack.roster.Roster;
|
|||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||
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.trust.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||
|
@ -185,7 +185,7 @@ public class OmemoManagerSetupHelper {
|
|||
// ignore
|
||||
}
|
||||
|
||||
CachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
||||
OmemoCachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
||||
.loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid());
|
||||
|
||||
for (int id : deviceList.getAllDevices()) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* 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;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
|
@ -18,7 +18,7 @@
|
|||
* 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;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
|
@ -18,7 +18,7 @@
|
|||
* 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;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
|
@ -31,7 +31,6 @@ import java.security.InvalidAlgorithmParameterException;
|
|||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
|
@ -42,8 +41,6 @@ import org.jivesoftware.smack.XMPPException;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
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.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
||||
|
@ -54,7 +51,7 @@ import org.junit.Test;
|
|||
/**
|
||||
* Test OmemoManager functionality.
|
||||
*/
|
||||
public class OmemoManagerTest extends SmackTestSuite {
|
||||
public class SignalOmemoManagerTest extends SmackTestSuite {
|
||||
|
||||
@Test
|
||||
public void instantiationTest()
|
|
@ -18,7 +18,7 @@
|
|||
* 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;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertTrue;
|
|
@ -18,7 +18,7 @@
|
|||
* 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;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -26,7 +26,6 @@ import java.io.IOException;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
|
@ -23,7 +23,7 @@ import java.util.TreeMap;
|
|||
import java.util.TreeSet;
|
||||
|
||||
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.util.OmemoKeyUtil;
|
||||
|
||||
|
@ -345,8 +345,8 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
|
|||
}
|
||||
|
||||
@Override
|
||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||
CachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
|
||||
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||
OmemoCachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
|
||||
|
||||
if (list == null && persistent != null) {
|
||||
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
|
||||
public void storeCachedDeviceList(OmemoDevice userDevice,
|
||||
BareJid contact,
|
||||
CachedDeviceList deviceList) {
|
||||
getCache(userDevice).deviceLists.put(contact, new CachedDeviceList(deviceList));
|
||||
OmemoCachedDeviceList deviceList) {
|
||||
getCache(userDevice).deviceLists.put(contact, new OmemoCachedDeviceList(deviceList));
|
||||
|
||||
if (persistent != null) {
|
||||
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<OmemoDevice, T_IdKey> identityKeys = 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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ import java.util.logging.Level;
|
|||
import java.util.logging.Logger;
|
||||
|
||||
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.jxmpp.jid.BareJid;
|
||||
|
@ -345,8 +345,8 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
|||
}
|
||||
|
||||
@Override
|
||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||
CachedDeviceList cachedDeviceList = new CachedDeviceList();
|
||||
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||
OmemoCachedDeviceList cachedDeviceList = new OmemoCachedDeviceList();
|
||||
|
||||
if (contact == 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
|
||||
public void storeCachedDeviceList(OmemoDevice userDevice,
|
||||
BareJid contact,
|
||||
CachedDeviceList contactsDeviceList) {
|
||||
OmemoCachedDeviceList contactsDeviceList) {
|
||||
if (contact == null) {
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -54,11 +54,13 @@ public final class OmemoConfiguration {
|
|||
|
||||
/**
|
||||
* Add Explicit Message Encryption hint (XEP-0380) to the message.
|
||||
* TODO: REMOVE
|
||||
*/
|
||||
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.
|
||||
* TODO: REMOVE
|
||||
*/
|
||||
private static boolean ADD_MAM_STORAGE_HINT = true;
|
||||
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
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.PEP_NODE_DEVICE_LIST_NOTIFY;
|
||||
|
||||
|
@ -26,11 +26,9 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||
|
@ -40,7 +38,6 @@ import org.jivesoftware.smack.StanzaListener;
|
|||
import org.jivesoftware.smack.XMPPConnection;
|
||||
import org.jivesoftware.smack.XMPPException;
|
||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.NamedElement;
|
||||
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.eme.element.ExplicitMessageEncryptionElement;
|
||||
import org.jivesoftware.smackx.hints.element.StoreHint;
|
||||
import org.jivesoftware.smackx.mam.MamManager;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||
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.exceptions.CannotEstablishOmemoSessionException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||
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.internal.CachedDeviceList;
|
||||
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.OmemoMessageInformation;
|
||||
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.TrustCallback;
|
||||
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
||||
import org.jivesoftware.smackx.pep.PEPListener;
|
||||
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.packet.PubSub;
|
||||
|
||||
|
@ -134,7 +124,7 @@ public final class OmemoManager extends Manager {
|
|||
});
|
||||
}
|
||||
|
||||
service.registerManager(this);
|
||||
service.registerRatchetForManager(this);
|
||||
|
||||
// StanzaListeners
|
||||
startStanzaListeners();
|
||||
|
@ -217,6 +207,14 @@ public final class OmemoManager extends Manager {
|
|||
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.
|
||||
*
|
||||
|
@ -237,7 +235,7 @@ public final class OmemoManager extends Manager {
|
|||
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.
|
||||
* 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
|
||||
* @return encrypted message
|
||||
* @throws CryptoFailedException when something crypto related fails
|
||||
|
@ -277,17 +286,15 @@ public final class OmemoManager extends Manager {
|
|||
* @throws SmackException.NotConnectedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public Message encrypt(BareJid to, String message)
|
||||
public OmemoMessage.Sent encrypt(BareJid recipient, String message)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
LoggedInOmemoManager guard = new LoggedInOmemoManager(this);
|
||||
Message plaintext = new Message();
|
||||
plaintext.setBody(message);
|
||||
OmemoElement encrypted = getOmemoService().processSendingMessage(guard, to, plaintext);
|
||||
return finishMessage(encrypted);
|
||||
ArrayList<BareJid> recipients = new ArrayList<>();
|
||||
recipients.add(recipient);
|
||||
return encrypt(recipients, message);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -306,17 +313,18 @@ public final class OmemoManager extends Manager {
|
|||
* @throws SmackException.NotConnectedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public Message encrypt(ArrayList<BareJid> recipients, String message)
|
||||
public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
Message m = new Message();
|
||||
m.setBody(message);
|
||||
OmemoElement encrypted = getOmemoService().processSendingMessage(
|
||||
new LoggedInOmemoManager(this), recipients, m);
|
||||
return finishMessage(encrypted);
|
||||
LoggedInOmemoManager guard = new LoggedInOmemoManager(this);
|
||||
List<OmemoDevice> devices = getDevicesOf(getOwnJid());
|
||||
for (BareJid recipient : recipients) {
|
||||
devices.addAll(getDevicesOf(recipient));
|
||||
}
|
||||
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
|
||||
* with any of their devices.
|
||||
*/
|
||||
public Message encrypt(MultiUserChat muc, String message)
|
||||
public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
|
||||
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
|
||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
|
||||
|
@ -348,8 +356,6 @@ public final class OmemoManager extends Manager {
|
|||
throw new NoOmemoSupportException();
|
||||
}
|
||||
|
||||
Message m = new Message();
|
||||
m.setBody(message);
|
||||
ArrayList<BareJid> recipients = new ArrayList<>();
|
||||
|
||||
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.
|
||||
* 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
|
||||
*/
|
||||
public void sendRatchetUpdateMessage(OmemoDevice recipient)
|
||||
throws CorruptedOmemoKeyException, UndecidedOmemoIdentityException, CryptoFailedException,
|
||||
CannotEstablishOmemoSessionException, SmackException.NotLoggedInException
|
||||
throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException,
|
||||
SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException,
|
||||
CryptoFailedException, CannotEstablishOmemoSessionException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
getOmemoService().sendOmemoRatchetUpdateMessage(
|
||||
new LoggedInOmemoManager(this), recipient, false);
|
||||
Message message = new Message();
|
||||
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.
|
||||
*
|
||||
|
@ -577,15 +472,12 @@ public final class OmemoManager extends Manager {
|
|||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public boolean contactSupportsOmemo(BareJid contact)
|
||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
||||
SmackException.NotLoggedInException
|
||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this);
|
||||
|
||||
getOmemoService().refreshDeviceList(managerGuard, contact);
|
||||
return !getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact)
|
||||
.getActiveDevices().isEmpty();
|
||||
OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact);
|
||||
return !deviceList.getActiveDevices().isEmpty();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -657,7 +549,7 @@ public final class OmemoManager extends Manager {
|
|||
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<>();
|
||||
CachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend()
|
||||
OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend()
|
||||
.loadCachedDeviceList(getOwnDevice(), contact);
|
||||
|
||||
for (int id : deviceList.getActiveDevices()) {
|
||||
|
@ -708,45 +600,28 @@ public final class OmemoManager extends Manager {
|
|||
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.
|
||||
*
|
||||
* @param contact contact we want to obtain the deviceList from.
|
||||
* @throws SmackException.NotConnectedException
|
||||
* @throws InterruptedException
|
||||
* @throws PubSubException.NotALeafNodeException
|
||||
* @throws XMPPException.XMPPErrorException
|
||||
* @throws SmackException.NotConnectedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public void requestDeviceListUpdateFor(BareJid contact)
|
||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
||||
SmackException.NotLoggedInException
|
||||
{
|
||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||
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).
|
||||
* The old signedPreKey should be kept for some more time (a month or so) to enable decryption of messages
|
||||
* that have been sent since the key was changed.
|
||||
* Rotate the signedPreKey published in our OmemoBundle and republish it. This should be done every now and
|
||||
* then (7-14 days). The old signedPreKey should be kept for some more time (a month or so) to enable decryption
|
||||
* of messages that have been sent since the key was changed.
|
||||
*
|
||||
* @throws CorruptedOmemoKeyException When the IdentityKeyPair is damaged.
|
||||
* @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
|
||||
*/
|
||||
public void rotateSignedPreKey()
|
||||
throws CorruptedOmemoKeyException, InterruptedException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException,
|
||||
PubSubException.NotALeafNodeException, SmackException.NotLoggedInException
|
||||
throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this);
|
||||
if (!connection().isAuthenticated()) {
|
||||
throw new SmackException.NotLoggedInException();
|
||||
}
|
||||
|
||||
// generate key
|
||||
getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice());
|
||||
|
||||
// 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
|
||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||
pepManager.removePEPListener(deviceListUpdateListener);
|
||||
//pepManager.removePEPListener(deviceListUpdateListener);
|
||||
|
||||
// Add listeners
|
||||
pepManager.addPEPListener(deviceListUpdateListener);
|
||||
//pepManager.addPEPListener(deviceListUpdateListener);
|
||||
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
|
||||
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||
}
|
||||
|
@ -932,7 +811,7 @@ public final class OmemoManager extends Manager {
|
|||
* Remove active stanza listeners needed for OMEMO.
|
||||
*/
|
||||
public void stopListeners() {
|
||||
PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
||||
//PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||
}
|
||||
|
@ -956,90 +835,6 @@ public final class OmemoManager extends Manager {
|
|||
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() {
|
||||
@Override
|
||||
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -68,10 +68,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
int keyId = omemoManager.getDeviceId();
|
||||
byte[] unpackedKey = null;
|
||||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||
// CHECKSTYLE: OFF
|
||||
@SuppressWarnings("unchecked")
|
||||
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
||||
// CHECKSTYLE: ON
|
||||
|
||||
// Find key with our ID.
|
||||
for (OmemoKeyElement k : keys) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -30,7 +30,8 @@ import org.jivesoftware.smackx.omemo.element.OmemoBundleElement_VAxolotl;
|
|||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||
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.trust.OmemoFingerprint;
|
||||
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
|
||||
BareJid ownJid = userDevice.getJid();
|
||||
CachedDeviceList cachedDeviceList;
|
||||
OmemoCachedDeviceList cachedDeviceList;
|
||||
|
||||
cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);
|
||||
|
||||
if (cachedDeviceList == null) {
|
||||
cachedDeviceList = new CachedDeviceList();
|
||||
cachedDeviceList = new OmemoCachedDeviceList();
|
||||
}
|
||||
// Does the list already contain that 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 list List we received.
|
||||
*/
|
||||
CachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
|
||||
CachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
|
||||
OmemoCachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
|
||||
OmemoCachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
|
||||
|
||||
if (cached == null) {
|
||||
cached = new CachedDeviceList();
|
||||
cached = new OmemoCachedDeviceList();
|
||||
}
|
||||
|
||||
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.
|
||||
* 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.
|
||||
* @return OmemoBundleElement
|
||||
|
@ -176,8 +175,6 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
createMissingKeys(userDevice);
|
||||
|
||||
int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice);
|
||||
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
|
||||
{
|
||||
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
|
||||
* @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.
|
||||
* @param userDevice
|
||||
* @return
|
||||
*/
|
||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) {
|
||||
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) {
|
||||
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,
|
||||
BareJid contact,
|
||||
CachedDeviceList contactsDeviceList);
|
||||
OmemoCachedDeviceList contactsDeviceList);
|
||||
|
||||
/**
|
||||
* 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 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.
|
||||
*
|
||||
|
@ -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
|
||||
* @return fingerprint of the identityKey
|
||||
*/
|
||||
public OmemoFingerprint getFingerprint(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
|
||||
public OmemoFingerprint getFingerprintAndMaybeBuildSession(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
|
||||
throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException
|
||||
{
|
||||
OmemoManager omemoManager = managerGuard.get();
|
||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
|||
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||
import org.jivesoftware.smack.util.Objects;
|
||||
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.
|
||||
|
@ -45,6 +46,10 @@ public abstract class OmemoDeviceListElement implements ExtensionElement {
|
|||
this.deviceIds = Collections.unmodifiableSet(deviceIds);
|
||||
}
|
||||
|
||||
public OmemoDeviceListElement(OmemoCachedDeviceList cachedList) {
|
||||
this.deviceIds = Collections.unmodifiableSet(cachedList.getActiveDevices());
|
||||
}
|
||||
|
||||
public Set<Integer> getDeviceIds() {
|
||||
return deviceIds;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_
|
|||
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||
|
||||
/**
|
||||
* The OMEMO device list element with the legacy Axolotl namespace.
|
||||
*
|
||||
|
@ -31,6 +33,10 @@ public class OmemoDeviceListElement_VAxolotl extends OmemoDeviceListElement {
|
|||
super(deviceIds);
|
||||
}
|
||||
|
||||
public OmemoDeviceListElement_VAxolotl(OmemoCachedDeviceList cachedList) {
|
||||
super(cachedList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return OMEMO_NAMESPACE_V_AXOLOTL;
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package org.jivesoftware.smackx.omemo.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.jivesoftware.smack.packet.NamedElement;
|
||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||
|
@ -33,10 +34,10 @@ public abstract class OmemoHeaderElement implements NamedElement {
|
|||
public static final String ATTR_IV = "iv";
|
||||
|
||||
private final int sid;
|
||||
private final ArrayList<OmemoKeyElement> keys;
|
||||
private final List<OmemoKeyElement> keys;
|
||||
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.keys = keys;
|
||||
this.iv = iv;
|
||||
|
@ -79,4 +80,4 @@ public abstract class OmemoHeaderElement implements NamedElement {
|
|||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,11 +16,11 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.element;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -65,4 +65,4 @@ public class OmemoKeyElement implements NamedElement {
|
|||
sb.closeElement(this);
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,6 @@ public class CannotEstablishOmemoSessionException extends Exception {
|
|||
private final HashMap<BareJid, HashMap<OmemoDevice, Throwable>> failures = new HashMap<>();
|
||||
private final HashMap<BareJid, ArrayList<OmemoDevice>> successes = new HashMap<>();
|
||||
|
||||
public CannotEstablishOmemoSessionException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) {
|
||||
super();
|
||||
getFailsOfContact(failed.getJid()).put(failed, reason);
|
||||
|
|
|
@ -16,6 +16,10 @@
|
|||
*/
|
||||
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.
|
||||
*
|
||||
|
@ -25,11 +29,18 @@ public class CryptoFailedException extends Exception {
|
|||
|
||||
private static final long serialVersionUID = 3466888654338119924L;
|
||||
|
||||
private final ArrayList<Exception> exceptions = new ArrayList<>();
|
||||
|
||||
public CryptoFailedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CryptoFailedException(Exception e) {
|
||||
super(e);
|
||||
exceptions.add(e);
|
||||
}
|
||||
|
||||
public List<Exception> getExceptions() {
|
||||
return Collections.unmodifiableList(exceptions);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,25 +16,18 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MultipleIOException extends IOException {
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
public class NoIdentityKeyException extends Exception {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ArrayList<IOException> exceptions = new ArrayList<>();
|
||||
private final OmemoDevice device;
|
||||
|
||||
public MultipleIOException(IOException... exceptions) {
|
||||
this.exceptions.addAll(Arrays.asList(exceptions));
|
||||
public NoIdentityKeyException(OmemoDevice device) {
|
||||
this.device = device;
|
||||
}
|
||||
|
||||
public void addException(IOException e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
|
||||
public ArrayList<IOException> getExceptions() {
|
||||
return exceptions;
|
||||
public OmemoDevice getDevice() {
|
||||
return device;
|
||||
}
|
||||
}
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.exceptions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
@ -34,6 +35,11 @@ public class UndecidedOmemoIdentityException extends Exception {
|
|||
this.devices.add(contact);
|
||||
}
|
||||
|
||||
public UndecidedOmemoIdentityException(Collection<OmemoDevice> devices) {
|
||||
super();
|
||||
this.devices.addAll(devices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the HashSet of undecided devices.
|
||||
*
|
||||
|
|
|
@ -20,8 +20,9 @@ import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
|||
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
|
||||
* we previously trusted.
|
||||
* Exception that gets thrown when we try to en-/decrypt a message for an untrusted contact.
|
||||
* 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 {
|
||||
|
||||
|
@ -30,7 +31,8 @@ public class UntrustedOmemoIdentityException extends Exception {
|
|||
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 fpTrusted fingerprint of the identityKey we previously had and trusted.
|
||||
* @param fpUntrusted fingerprint of the new key which is untrusted.
|
||||
|
@ -42,6 +44,16 @@ public class UntrustedOmemoIdentityException extends Exception {
|
|||
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 omemoDevice.
|
||||
|
@ -52,6 +64,7 @@ public class UntrustedOmemoIdentityException extends Exception {
|
|||
|
||||
/**
|
||||
* Return the fingerprint of the key we expected.
|
||||
* This might return null in case this exception got thrown during encryption process.
|
||||
* @return
|
||||
*/
|
||||
public OmemoFingerprint getTrustedFingerprint() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -32,24 +32,24 @@ import java.util.Set;
|
|||
*
|
||||
* @author Paul Schaub
|
||||
*/
|
||||
public class CachedDeviceList implements Serializable {
|
||||
public class OmemoCachedDeviceList implements Serializable {
|
||||
private static final long serialVersionUID = 3153579238321261203L;
|
||||
|
||||
private final Set<Integer> activeDevices;
|
||||
private final Set<Integer> inactiveDevices;
|
||||
|
||||
public CachedDeviceList() {
|
||||
public OmemoCachedDeviceList() {
|
||||
this.activeDevices = new HashSet<>();
|
||||
this.inactiveDevices = new HashSet<>();
|
||||
}
|
||||
|
||||
public CachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
|
||||
public OmemoCachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
|
||||
this();
|
||||
this.activeDevices.addAll(activeDevices);
|
||||
this.inactiveDevices.addAll(inactiveDevices);
|
||||
}
|
||||
|
||||
public CachedDeviceList(CachedDeviceList original) {
|
||||
public OmemoCachedDeviceList(OmemoCachedDeviceList original) {
|
||||
this(original.getActiveDevices(), original.getInactiveDevices());
|
||||
}
|
||||
|
||||
|
@ -109,6 +109,11 @@ public class CachedDeviceList implements Serializable {
|
|||
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.
|
||||
*
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
/**
|
||||
|
@ -74,4 +76,12 @@ public class OmemoDevice {
|
|||
i = jid.hashCode() + deviceId;
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,21 +37,21 @@ import javax.crypto.SecretKey;
|
|||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
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_VAxolotl;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoHeaderElement_VAxolotl;
|
||||
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.CryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
|
||||
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.OmemoDevice;
|
||||
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
|
||||
*/
|
||||
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 byte[] initializationVector = generateIv();
|
||||
private final OmemoDevice userDevice;
|
||||
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 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 BadPaddingException
|
||||
* @throws InvalidKeyException
|
||||
|
@ -94,24 +99,31 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @throws NoSuchProviderException
|
||||
* @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,
|
||||
byte[] aesKey, byte[] iv)
|
||||
byte[] aesKey,
|
||||
byte[] iv,
|
||||
String message)
|
||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
||||
IllegalBlockSizeException,
|
||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
this.managerGuard = managerGuard;
|
||||
this.userDevice = userDevice;
|
||||
this.trustCallback = callback;
|
||||
this.ratchet = ratchet;
|
||||
this.messageKey = aesKey;
|
||||
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 BadPaddingException
|
||||
* @throws InvalidKeyException
|
||||
|
@ -121,31 +133,32 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @throws NoSuchProviderException
|
||||
* @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,
|
||||
String message)
|
||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
this.managerGuard = managerGuard;
|
||||
this.ratchet = ratchet;
|
||||
this.setMessage(message);
|
||||
this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an AES messageKey and use it to encrypt the message.
|
||||
* Optionally append the Auth Tag of the encrypted message to the messageKey afterwards.
|
||||
* Encrypt the message with the aes key.
|
||||
* 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
|
||||
* @throws NoSuchPaddingException When no Cipher could be instantiated.
|
||||
* @throws NoSuchAlgorithmException when no Cipher could be instantiated.
|
||||
* @throws NoSuchProviderException when BouncyCastle could not be found.
|
||||
* @throws InvalidAlgorithmParameterException when the Cipher could not be initialized
|
||||
* @throws InvalidKeyException when the generated key is invalid
|
||||
* @throws UnsupportedEncodingException when UTF8 is unavailable
|
||||
* @throws BadPaddingException when cipher.doFinal gets wrong padding
|
||||
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size.
|
||||
* @param message plaintext message
|
||||
* @throws NoSuchPaddingException
|
||||
* @throws NoSuchAlgorithmException
|
||||
* @throws NoSuchProviderException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
* @throws InvalidKeyException
|
||||
* @throws UnsupportedEncodingException
|
||||
* @throws BadPaddingException
|
||||
* @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) {
|
||||
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[] cipherTextWithoutAuthTag = new byte[ciphertext.length - 16];
|
||||
|
||||
System.arraycopy(messageKey, 0, clearKeyWithAuthTag, 0, 16);
|
||||
System.arraycopy(ciphertext, 0, cipherTextWithoutAuthTag, 0, cipherTextWithoutAuthTag.length);
|
||||
System.arraycopy(ciphertext, ciphertext.length - 16, clearKeyWithAuthTag, 16, 16);
|
||||
moveAuthTag(messageKey, ciphertext, clearKeyWithAuthTag, cipherTextWithoutAuthTag);
|
||||
|
||||
ciphertextMessage = cipherTextWithoutAuthTag;
|
||||
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
|
||||
* @throws CryptoFailedException when encrypting the messageKey fails
|
||||
* @throws UndecidedOmemoIdentityException
|
||||
* @throws CorruptedOmemoKeyException
|
||||
* @param messageKey source messageKey without authTag
|
||||
* @param cipherText source cipherText with authTag
|
||||
* @param messageKeyWithAuthTag destination messageKey with authTag
|
||||
* @param cipherTextWithoutAuthTag destination cipherText without authTag
|
||||
*/
|
||||
public void addRecipient(OmemoDevice device)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
||||
CannotEstablishOmemoSessionException {
|
||||
addRecipient(device, false);
|
||||
static void moveAuthTag(byte[] messageKey,
|
||||
byte[] cipherText,
|
||||
byte[] messageKeyWithAuthTag,
|
||||
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.
|
||||
* @param contactsDevice recipient device
|
||||
* @param ignoreTrust ignore current trust state? Useful for keyTransportMessages that are sent to repair a session
|
||||
* @throws CryptoFailedException
|
||||
* @throws UndecidedOmemoIdentityException
|
||||
* @throws CorruptedOmemoKeyException
|
||||
*
|
||||
* @param contactsDevice device of the recipient
|
||||
* @throws NoIdentityKeyException if we have no identityKey of that device. Can be fixed by fetching and
|
||||
* processing the devices bundle.
|
||||
* @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
|
||||
CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
||||
CannotEstablishOmemoSessionException {
|
||||
public void addRecipient(OmemoDevice contactsDevice)
|
||||
throws NoIdentityKeyException, CorruptedOmemoKeyException, UndecidedOmemoIdentityException,
|
||||
UntrustedOmemoIdentityException {
|
||||
|
||||
OmemoFingerprint fingerprint;
|
||||
try {
|
||||
fingerprint = managerGuard.get().getFingerprint(contactsDevice);
|
||||
} catch (SmackException.NotLoggedInException e) {
|
||||
throw new AssertionError("This should never happen.");
|
||||
}
|
||||
fingerprint = OmemoService.getInstance().getOmemoStoreBackend().getFingerprint(userDevice, contactsDevice);
|
||||
|
||||
if (!ignoreTrust && !managerGuard.get().isDecidedOmemoIdentity(contactsDevice, fingerprint)) {
|
||||
// Warn user of undecided device
|
||||
throw new UndecidedOmemoIdentityException(contactsDevice);
|
||||
}
|
||||
switch (trustCallback.getTrust(contactsDevice, fingerprint)) {
|
||||
|
||||
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() {
|
||||
OmemoHeaderElement_VAxolotl header = new OmemoHeaderElement_VAxolotl(
|
||||
managerGuard.get().getDeviceId(),
|
||||
userDevice.getDeviceId(),
|
||||
keys,
|
||||
initializationVector
|
||||
);
|
||||
|
@ -238,9 +265,9 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @return new AES key
|
||||
* @throws NoSuchAlgorithmException
|
||||
*/
|
||||
public static byte[] generateKey() throws NoSuchAlgorithmException {
|
||||
KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
|
||||
generator.init(KEYLENGTH);
|
||||
public static byte[] generateKey(String keyType, int keyLength) throws NoSuchAlgorithmException {
|
||||
KeyGenerator generator = KeyGenerator.getInstance(keyType);
|
||||
generator.init(keyLength);
|
||||
return generator.generateKey().getEncoded();
|
||||
}
|
||||
|
||||
|
@ -255,12 +282,4 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
random.nextBytes(iv);
|
||||
return iv;
|
||||
}
|
||||
|
||||
public byte[] getCiphertextMessage() {
|
||||
return ciphertextMessage;
|
||||
}
|
||||
|
||||
public byte[] getMessageKey() {
|
||||
return messageKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertNotNull;
|
||||
|
@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
|
@ -43,7 +43,7 @@ public class DeviceListTest {
|
|||
*/
|
||||
@Test
|
||||
public void mergeDeviceListsTest() {
|
||||
CachedDeviceList cached = new CachedDeviceList();
|
||||
OmemoCachedDeviceList cached = new OmemoCachedDeviceList();
|
||||
assertNotNull(cached.getActiveDevices());
|
||||
assertNotNull(cached.getInactiveDevices());
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertNull;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertTrue;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertTrue;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.fail;
|
||||
import static org.junit.Assert.assertEquals;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertNotSame;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertNotNull;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertNull;
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertFalse;
|
||||
|
@ -31,10 +31,8 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
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.internal.CachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
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 {
|
||||
Integer[] active = new Integer[] {1,5,999,10};
|
||||
Integer[] inactive = new Integer[] {6,7,8};
|
||||
CachedDeviceList before = new CachedDeviceList(
|
||||
OmemoCachedDeviceList before = new OmemoCachedDeviceList(
|
||||
new HashSet<>(Arrays.asList(active)),
|
||||
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.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);
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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,
|
||||
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* 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.assertTrue;
|
|
@ -14,7 +14,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo.util;
|
||||
package org.jivesoftware.smackx.omemo.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue