mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-23 20:42:06 +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.smack.roster.RosterEntry;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||||
|
@ -185,7 +185,7 @@ public class OmemoManagerSetupHelper {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
CachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
OmemoCachedDeviceList deviceList = OmemoService.getInstance().getOmemoStoreBackend()
|
||||||
.loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid());
|
.loadCachedDeviceList(omemoManager.getOwnDevice(), omemoManager.getOwnJid());
|
||||||
|
|
||||||
for (int id : deviceList.getAllDevices()) {
|
for (int id : deviceList.getAllDevices()) {
|
||||||
|
|
|
@ -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,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertNotNull;
|
import static junit.framework.TestCase.assertNotNull;
|
|
@ -18,7 +18,7 @@
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
|
@ -18,7 +18,7 @@
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertFalse;
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
@ -31,7 +31,6 @@ import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.InvalidKeyException;
|
import java.security.InvalidKeyException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
|
@ -42,8 +41,6 @@ import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
||||||
|
@ -54,7 +51,7 @@ import org.junit.Test;
|
||||||
/**
|
/**
|
||||||
* Test OmemoManager functionality.
|
* Test OmemoManager functionality.
|
||||||
*/
|
*/
|
||||||
public class OmemoManagerTest extends SmackTestSuite {
|
public class SignalOmemoManagerTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void instantiationTest()
|
public void instantiationTest()
|
|
@ -18,7 +18,7 @@
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
|
@ -18,7 +18,7 @@
|
||||||
* along with this program; if not, write to the Free Software Foundation,
|
* along with this program; if not, write to the Free Software Foundation,
|
||||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
@ -26,7 +26,6 @@ import java.io.IOException;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
|
||||||
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
|
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
|
||||||
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
|
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
|
||||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
|
@ -23,7 +23,7 @@ import java.util.TreeMap;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
||||||
|
|
||||||
|
@ -345,8 +345,8 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||||
CachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
|
OmemoCachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
|
||||||
|
|
||||||
if (list == null && persistent != null) {
|
if (list == null && persistent != null) {
|
||||||
list = persistent.loadCachedDeviceList(userDevice, contact);
|
list = persistent.loadCachedDeviceList(userDevice, contact);
|
||||||
|
@ -355,14 +355,14 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return list == null ? new CachedDeviceList() : new CachedDeviceList(list);
|
return list == null ? new OmemoCachedDeviceList() : new OmemoCachedDeviceList(list);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void storeCachedDeviceList(OmemoDevice userDevice,
|
public void storeCachedDeviceList(OmemoDevice userDevice,
|
||||||
BareJid contact,
|
BareJid contact,
|
||||||
CachedDeviceList deviceList) {
|
OmemoCachedDeviceList deviceList) {
|
||||||
getCache(userDevice).deviceLists.put(contact, new CachedDeviceList(deviceList));
|
getCache(userDevice).deviceLists.put(contact, new OmemoCachedDeviceList(deviceList));
|
||||||
|
|
||||||
if (persistent != null) {
|
if (persistent != null) {
|
||||||
persistent.storeCachedDeviceList(userDevice, contact, deviceList);
|
persistent.storeCachedDeviceList(userDevice, contact, deviceList);
|
||||||
|
@ -417,7 +417,7 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
|
||||||
private final HashMap<BareJid, HashMap<Integer, T_Sess>> sessions = new HashMap<>();
|
private final HashMap<BareJid, HashMap<Integer, T_Sess>> sessions = new HashMap<>();
|
||||||
private final HashMap<OmemoDevice, T_IdKey> identityKeys = new HashMap<>();
|
private final HashMap<OmemoDevice, T_IdKey> identityKeys = new HashMap<>();
|
||||||
private final HashMap<OmemoDevice, Date> lastMessagesDates = new HashMap<>();
|
private final HashMap<OmemoDevice, Date> lastMessagesDates = new HashMap<>();
|
||||||
private final HashMap<BareJid, CachedDeviceList> deviceLists = new HashMap<>();
|
private final HashMap<BareJid, OmemoCachedDeviceList> deviceLists = new HashMap<>();
|
||||||
private Date lastRenewalDate = null;
|
private Date lastRenewalDate = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
@ -345,8 +345,8 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||||
CachedDeviceList cachedDeviceList = new CachedDeviceList();
|
OmemoCachedDeviceList cachedDeviceList = new OmemoCachedDeviceList();
|
||||||
|
|
||||||
if (contact == null) {
|
if (contact == null) {
|
||||||
throw new IllegalArgumentException("Contact can not be null.");
|
throw new IllegalArgumentException("Contact can not be null.");
|
||||||
|
@ -372,7 +372,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
|
||||||
@Override
|
@Override
|
||||||
public void storeCachedDeviceList(OmemoDevice userDevice,
|
public void storeCachedDeviceList(OmemoDevice userDevice,
|
||||||
BareJid contact,
|
BareJid contact,
|
||||||
CachedDeviceList contactsDeviceList) {
|
OmemoCachedDeviceList contactsDeviceList) {
|
||||||
if (contact == null) {
|
if (contact == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,11 +54,13 @@ public final class OmemoConfiguration {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add Explicit Message Encryption hint (XEP-0380) to the message.
|
* Add Explicit Message Encryption hint (XEP-0380) to the message.
|
||||||
|
* TODO: REMOVE
|
||||||
*/
|
*/
|
||||||
private static boolean ADD_EME_ENCRYPTION_HINT = true;
|
private static boolean ADD_EME_ENCRYPTION_HINT = true;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add MAM storage hint to allow the server to store messages that do not contain a body.
|
* Add MAM storage hint to allow the server to store messages that do not contain a body.
|
||||||
|
* TODO: REMOVE
|
||||||
*/
|
*/
|
||||||
private static boolean ADD_MAM_STORAGE_HINT = true;
|
private static boolean ADD_MAM_STORAGE_HINT = true;
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.BODY_OMEMO_HINT;
|
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
|
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_V_AXOLOTL;
|
||||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY;
|
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_LIST_NOTIFY;
|
||||||
|
|
||||||
|
@ -26,11 +26,9 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
import java.util.logging.Level;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||||
|
@ -40,7 +38,6 @@ import org.jivesoftware.smack.StanzaListener;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
@ -51,21 +48,18 @@ import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||||
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
import org.jivesoftware.smackx.disco.ServiceDiscoveryManager;
|
||||||
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
import org.jivesoftware.smackx.eme.element.ExplicitMessageEncryptionElement;
|
||||||
import org.jivesoftware.smackx.hints.element.StoreHint;
|
import org.jivesoftware.smackx.hints.element.StoreHint;
|
||||||
import org.jivesoftware.smackx.mam.MamManager;
|
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||||
import org.jivesoftware.smackx.muc.RoomInfo;
|
import org.jivesoftware.smackx.muc.RoomInfo;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException;
|
import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||||
import org.jivesoftware.smackx.omemo.internal.ClearTextMessage;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
||||||
|
@ -73,11 +67,7 @@ import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
||||||
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
||||||
import org.jivesoftware.smackx.pep.PEPListener;
|
|
||||||
import org.jivesoftware.smackx.pep.PEPManager;
|
import org.jivesoftware.smackx.pep.PEPManager;
|
||||||
import org.jivesoftware.smackx.pubsub.EventElement;
|
|
||||||
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
|
||||||
|
@ -134,7 +124,7 @@ public final class OmemoManager extends Manager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
service.registerManager(this);
|
service.registerRatchetForManager(this);
|
||||||
|
|
||||||
// StanzaListeners
|
// StanzaListeners
|
||||||
startStanzaListeners();
|
startStanzaListeners();
|
||||||
|
@ -217,6 +207,14 @@ public final class OmemoManager extends Manager {
|
||||||
trustCallback = callback;
|
trustCallback = callback;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the TrustCallback of this manager.
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
TrustCallback getTrustCallback() {
|
||||||
|
return trustCallback;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initializes the OmemoManager. This method must be called before the manager can be used.
|
* Initializes the OmemoManager. This method must be called before the manager can be used.
|
||||||
*
|
*
|
||||||
|
@ -237,7 +235,7 @@ public final class OmemoManager extends Manager {
|
||||||
throw new SmackException.NotLoggedInException();
|
throw new SmackException.NotLoggedInException();
|
||||||
}
|
}
|
||||||
|
|
||||||
getOmemoService().publish(new LoggedInOmemoManager(this));
|
getOmemoService().init(new LoggedInOmemoManager(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -261,11 +259,22 @@ public final class OmemoManager extends Manager {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<OmemoDevice> getDevicesOf(BareJid recipient) {
|
||||||
|
OmemoCachedDeviceList list = getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), recipient);
|
||||||
|
ArrayList<OmemoDevice> devices = new ArrayList<>();
|
||||||
|
|
||||||
|
for (int deviceId : list.getActiveDevices()) {
|
||||||
|
devices.add(new OmemoDevice(recipient, deviceId));
|
||||||
|
}
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OMEMO encrypt a cleartext message for a single recipient.
|
* OMEMO encrypt a cleartext message for a single recipient.
|
||||||
* Note that this method does NOT set the 'to' attribute of the message.
|
* Note that this method does NOT set the 'to' attribute of the message.
|
||||||
*
|
*
|
||||||
* @param to recipients bareJid
|
* @param recipient recipients bareJid
|
||||||
* @param message text to encrypt
|
* @param message text to encrypt
|
||||||
* @return encrypted message
|
* @return encrypted message
|
||||||
* @throws CryptoFailedException when something crypto related fails
|
* @throws CryptoFailedException when something crypto related fails
|
||||||
|
@ -277,17 +286,15 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws SmackException.NotConnectedException
|
* @throws SmackException.NotConnectedException
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public Message encrypt(BareJid to, String message)
|
public OmemoMessage.Sent encrypt(BareJid recipient, String message)
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
LoggedInOmemoManager guard = new LoggedInOmemoManager(this);
|
ArrayList<BareJid> recipients = new ArrayList<>();
|
||||||
Message plaintext = new Message();
|
recipients.add(recipient);
|
||||||
plaintext.setBody(message);
|
return encrypt(recipients, message);
|
||||||
OmemoElement encrypted = getOmemoService().processSendingMessage(guard, to, plaintext);
|
|
||||||
return finishMessage(encrypted);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -306,17 +313,18 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws SmackException.NotConnectedException
|
* @throws SmackException.NotConnectedException
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public Message encrypt(ArrayList<BareJid> recipients, String message)
|
public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
Message m = new Message();
|
LoggedInOmemoManager guard = new LoggedInOmemoManager(this);
|
||||||
m.setBody(message);
|
List<OmemoDevice> devices = getDevicesOf(getOwnJid());
|
||||||
OmemoElement encrypted = getOmemoService().processSendingMessage(
|
for (BareJid recipient : recipients) {
|
||||||
new LoggedInOmemoManager(this), recipients, m);
|
devices.addAll(getDevicesOf(recipient));
|
||||||
return finishMessage(encrypted);
|
}
|
||||||
|
return service.createOmemoMessage(guard, devices, message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -337,7 +345,7 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws CannotEstablishOmemoSessionException when there is a user for whom we could not create a session
|
* @throws CannotEstablishOmemoSessionException when there is a user for whom we could not create a session
|
||||||
* with any of their devices.
|
* with any of their devices.
|
||||||
*/
|
*/
|
||||||
public Message encrypt(MultiUserChat muc, String message)
|
public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
|
||||||
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
|
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||||
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
|
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
|
||||||
|
@ -348,8 +356,6 @@ public final class OmemoManager extends Manager {
|
||||||
throw new NoOmemoSupportException();
|
throw new NoOmemoSupportException();
|
||||||
}
|
}
|
||||||
|
|
||||||
Message m = new Message();
|
|
||||||
m.setBody(message);
|
|
||||||
ArrayList<BareJid> recipients = new ArrayList<>();
|
ArrayList<BareJid> recipients = new ArrayList<>();
|
||||||
|
|
||||||
for (EntityFullJid e : muc.getOccupants()) {
|
for (EntityFullJid e : muc.getOccupants()) {
|
||||||
|
@ -359,75 +365,6 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Encrypt a message for all users we could build a session with successfully in a previous attempt.
|
|
||||||
* This method can come in handy as a fallback when encrypting a message fails due to devices we cannot
|
|
||||||
* build a session with.
|
|
||||||
*
|
|
||||||
* @param exception CannotEstablishSessionException from a previous encrypt(user(s), message) call.
|
|
||||||
* @param message message we want to send.
|
|
||||||
* @return encrypted message
|
|
||||||
* @throws CryptoFailedException
|
|
||||||
* @throws UndecidedOmemoIdentityException when there are undecided identities.
|
|
||||||
*/
|
|
||||||
public Message encryptForExistingSessions(CannotEstablishOmemoSessionException exception, String message)
|
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, SmackException.NotLoggedInException
|
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
|
||||||
Message m = new Message();
|
|
||||||
m.setBody(message);
|
|
||||||
OmemoElement encrypted = getOmemoService()
|
|
||||||
.encryptOmemoMessage(new LoggedInOmemoManager(this), exception.getSuccesses(), m);
|
|
||||||
return finishMessage(encrypted);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Decrypt an OMEMO message. This method comes handy when dealing with messages that were not automatically
|
|
||||||
* decrypted by smack-omemo, eg. MAM query messages.
|
|
||||||
* @param sender sender of the message
|
|
||||||
* @param omemoMessage message
|
|
||||||
* @return decrypted message
|
|
||||||
* @throws InterruptedException Exception
|
|
||||||
* @throws SmackException.NoResponseException Exception
|
|
||||||
* @throws SmackException.NotConnectedException Exception
|
|
||||||
* @throws CryptoFailedException When decryption fails
|
|
||||||
* @throws XMPPException.XMPPErrorException Exception
|
|
||||||
* @throws CorruptedOmemoKeyException When the used keys are invalid
|
|
||||||
* @throws NoRawSessionException When there is no double ratchet session found for this message
|
|
||||||
*/
|
|
||||||
public ClearTextMessage decrypt(BareJid sender, Message omemoMessage)
|
|
||||||
throws InterruptedException, SmackException.NoResponseException, SmackException.NotConnectedException,
|
|
||||||
CryptoFailedException, XMPPException.XMPPErrorException, CorruptedOmemoKeyException, NoRawSessionException,
|
|
||||||
SmackException.NotLoggedInException
|
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
|
||||||
return getOmemoService().processLocalMessage(new LoggedInOmemoManager(this), sender, omemoMessage);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return a list of all OMEMO messages that were found in the MAM query result, that could be successfully
|
|
||||||
* decrypted. Normal cleartext messages are also added to this list.
|
|
||||||
*
|
|
||||||
* @param mamQueryResult mamQueryResult
|
|
||||||
* @return list of decrypted OmemoMessages
|
|
||||||
* @throws InterruptedException Exception
|
|
||||||
* @throws XMPPException.XMPPErrorException Exception
|
|
||||||
* @throws SmackException.NotConnectedException Exception
|
|
||||||
* @throws SmackException.NoResponseException Exception
|
|
||||||
*/
|
|
||||||
public List<ClearTextMessage> decryptMamQueryResult(MamManager.MamQueryResult mamQueryResult)
|
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
|
||||||
List<ClearTextMessage> l = new ArrayList<>();
|
|
||||||
l.addAll(getOmemoService().decryptMamQueryResult(new LoggedInOmemoManager(this), mamQueryResult));
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Trust that a fingerprint belongs to an OmemoDevice.
|
* Trust that a fingerprint belongs to an OmemoDevice.
|
||||||
* The fingerprint must be the lowercase, hexadecimal fingerprint of the identityKey of the device and must
|
* The fingerprint must be the lowercase, hexadecimal fingerprint of the identityKey of the device and must
|
||||||
|
@ -501,72 +438,30 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient
|
* @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient
|
||||||
*/
|
*/
|
||||||
public void sendRatchetUpdateMessage(OmemoDevice recipient)
|
public void sendRatchetUpdateMessage(OmemoDevice recipient)
|
||||||
throws CorruptedOmemoKeyException, UndecidedOmemoIdentityException, CryptoFailedException,
|
throws SmackException.NotLoggedInException, CorruptedOmemoKeyException, InterruptedException,
|
||||||
CannotEstablishOmemoSessionException, SmackException.NotLoggedInException
|
SmackException.NoResponseException, NoSuchAlgorithmException, SmackException.NotConnectedException,
|
||||||
|
CryptoFailedException, CannotEstablishOmemoSessionException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
getOmemoService().sendOmemoRatchetUpdateMessage(
|
Message message = new Message();
|
||||||
new LoggedInOmemoManager(this), recipient, false);
|
message.setFrom(getOwnJid());
|
||||||
|
message.setTo(recipient.getJid());
|
||||||
|
|
||||||
|
OmemoElement element = getOmemoService()
|
||||||
|
.createRatchetUpdateElement(new LoggedInOmemoManager(this), recipient);
|
||||||
|
message.addExtension(element);
|
||||||
|
|
||||||
|
// Set MAM Storage hint
|
||||||
|
StoreHint.set(message);
|
||||||
|
|
||||||
|
if (OmemoConfiguration.getAddEmeEncryptionHint()) {
|
||||||
|
message.addExtension(new ExplicitMessageEncryptionElement(
|
||||||
|
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
|
||||||
|
}
|
||||||
|
connection().sendStanza(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new KeyTransportElement. This message will contain the AES-Key and IV that can be used eg. for encrypted
|
|
||||||
* Jingle file transfer.
|
|
||||||
*
|
|
||||||
* @param aesKey AES key to transport
|
|
||||||
* @param iv Initialization vector
|
|
||||||
* @param to list of recipient devices
|
|
||||||
* @return KeyTransportMessage
|
|
||||||
* @throws UndecidedOmemoIdentityException When the trust of session with the recipient is not decided yet
|
|
||||||
* @throws CorruptedOmemoKeyException When the used identityKeys are corrupted
|
|
||||||
* @throws CryptoFailedException When something fails with the crypto
|
|
||||||
* @throws CannotEstablishOmemoSessionException When we can't establish a session with the recipient
|
|
||||||
*/
|
|
||||||
public OmemoElement createKeyTransportElement(byte[] aesKey, byte[] iv, OmemoDevice ... to)
|
|
||||||
throws UndecidedOmemoIdentityException, CorruptedOmemoKeyException, CryptoFailedException,
|
|
||||||
CannotEstablishOmemoSessionException, SmackException.NotLoggedInException
|
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
|
||||||
return getOmemoService().prepareOmemoKeyTransportElement(
|
|
||||||
new LoggedInOmemoManager(this), aesKey, iv, to);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new Message from a encrypted OmemoMessageElement.
|
|
||||||
* Add ourselves as the sender and the encrypted element.
|
|
||||||
* Also tell the server to store the message despite a possible missing body.
|
|
||||||
* The body will be set to a hint message that we are using OMEMO.
|
|
||||||
*
|
|
||||||
* @param encrypted OmemoMessageElement
|
|
||||||
* @return Message containing the OMEMO element and some additional information
|
|
||||||
*/
|
|
||||||
Message finishMessage(OmemoElement encrypted) {
|
|
||||||
if (encrypted == null) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
Message chatMessage = new Message();
|
|
||||||
chatMessage.setFrom(connection().getUser().asBareJid());
|
|
||||||
chatMessage.addExtension(encrypted);
|
|
||||||
|
|
||||||
if (OmemoConfiguration.getAddOmemoHintBody()) {
|
|
||||||
chatMessage.setBody(BODY_OMEMO_HINT);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OmemoConfiguration.getAddMAMStorageProcessingHint()) {
|
|
||||||
StoreHint.set(chatMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (OmemoConfiguration.getAddEmeEncryptionHint()) {
|
|
||||||
chatMessage.addExtension(new ExplicitMessageEncryptionElement(
|
|
||||||
ExplicitMessageEncryptionElement.ExplicitMessageEncryptionProtocol.omemoVAxolotl));
|
|
||||||
}
|
|
||||||
|
|
||||||
return chatMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true, if the contact has any active devices published in a deviceList.
|
* Returns true, if the contact has any active devices published in a deviceList.
|
||||||
*
|
*
|
||||||
|
@ -577,15 +472,12 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public boolean contactSupportsOmemo(BareJid contact)
|
public boolean contactSupportsOmemo(BareJid contact)
|
||||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotLoggedInException
|
SmackException.NotConnectedException, SmackException.NoResponseException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this);
|
OmemoCachedDeviceList deviceList = getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact);
|
||||||
|
return !deviceList.getActiveDevices().isEmpty();
|
||||||
getOmemoService().refreshDeviceList(managerGuard, contact);
|
|
||||||
return !getOmemoService().getOmemoStoreBackend().loadCachedDeviceList(getOwnDevice(), contact)
|
|
||||||
.getActiveDevices().isEmpty();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -657,7 +549,7 @@ public final class OmemoManager extends Manager {
|
||||||
return getOwnFingerprint();
|
return getOwnFingerprint();
|
||||||
}
|
}
|
||||||
|
|
||||||
return getOmemoService().getOmemoStoreBackend().getFingerprint(new LoggedInOmemoManager(this), device);
|
return getOmemoService().getOmemoStoreBackend().getFingerprintAndMaybeBuildSession(new LoggedInOmemoManager(this), device);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -676,7 +568,7 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
|
|
||||||
HashMap<OmemoDevice, OmemoFingerprint> fingerprints = new HashMap<>();
|
HashMap<OmemoDevice, OmemoFingerprint> fingerprints = new HashMap<>();
|
||||||
CachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend()
|
OmemoCachedDeviceList deviceList = getOmemoService().getOmemoStoreBackend()
|
||||||
.loadCachedDeviceList(getOwnDevice(), contact);
|
.loadCachedDeviceList(getOwnDevice(), contact);
|
||||||
|
|
||||||
for (int id : deviceList.getActiveDevices()) {
|
for (int id : deviceList.getActiveDevices()) {
|
||||||
|
@ -708,45 +600,28 @@ public final class OmemoManager extends Manager {
|
||||||
omemoMucMessageListeners.remove(listener);
|
omemoMucMessageListeners.remove(listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Build OMEMO sessions with devices of contact.
|
|
||||||
*
|
|
||||||
* @param contact contact we want to build session with.
|
|
||||||
* @throws InterruptedException
|
|
||||||
* @throws CannotEstablishOmemoSessionException
|
|
||||||
* @throws SmackException.NotConnectedException
|
|
||||||
* @throws SmackException.NoResponseException
|
|
||||||
*/
|
|
||||||
public void buildSessionsWith(BareJid contact)
|
|
||||||
throws InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
|
||||||
getOmemoService().buildMissingOmemoSessions(new LoggedInOmemoManager(this), contact);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Request a deviceList update from contact contact.
|
* Request a deviceList update from contact contact.
|
||||||
*
|
*
|
||||||
* @param contact contact we want to obtain the deviceList from.
|
* @param contact contact we want to obtain the deviceList from.
|
||||||
* @throws SmackException.NotConnectedException
|
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
|
* @throws PubSubException.NotALeafNodeException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public void requestDeviceListUpdateFor(BareJid contact)
|
public void requestDeviceListUpdateFor(BareJid contact)
|
||||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotLoggedInException
|
SmackException.NotConnectedException, SmackException.NoResponseException {
|
||||||
{
|
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
getOmemoService().refreshDeviceList(new LoggedInOmemoManager(this), contact);
|
getOmemoService().refreshDeviceList(connection(), getOwnDevice(), contact);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Rotate the signedPreKey published in our OmemoBundle. This should be done every now and then (7-14 days).
|
* Rotate the signedPreKey published in our OmemoBundle and republish it. This should be done every now and
|
||||||
* The old signedPreKey should be kept for some more time (a month or so) to enable decryption of messages
|
* then (7-14 days). The old signedPreKey should be kept for some more time (a month or so) to enable decryption
|
||||||
* that have been sent since the key was changed.
|
* of messages that have been sent since the key was changed.
|
||||||
*
|
*
|
||||||
* @throws CorruptedOmemoKeyException When the IdentityKeyPair is damaged.
|
* @throws CorruptedOmemoKeyException When the IdentityKeyPair is damaged.
|
||||||
* @throws InterruptedException XMPP error
|
* @throws InterruptedException XMPP error
|
||||||
|
@ -756,16 +631,20 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws PubSubException.NotALeafNodeException if the bundle node on the server is a CollectionNode
|
* @throws PubSubException.NotALeafNodeException if the bundle node on the server is a CollectionNode
|
||||||
*/
|
*/
|
||||||
public void rotateSignedPreKey()
|
public void rotateSignedPreKey()
|
||||||
throws CorruptedOmemoKeyException, InterruptedException, XMPPException.XMPPErrorException,
|
throws CorruptedOmemoKeyException, SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotConnectedException, SmackException.NoResponseException,
|
SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException
|
||||||
PubSubException.NotALeafNodeException, SmackException.NotLoggedInException
|
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
LoggedInOmemoManager managerGuard = new LoggedInOmemoManager(this);
|
if (!connection().isAuthenticated()) {
|
||||||
|
throw new SmackException.NotLoggedInException();
|
||||||
|
}
|
||||||
|
|
||||||
// generate key
|
// generate key
|
||||||
getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice());
|
getOmemoService().getOmemoStoreBackend().changeSignedPreKey(getOwnDevice());
|
||||||
|
|
||||||
// publish
|
// publish
|
||||||
getOmemoService().publish(managerGuard);
|
OmemoBundleElement bundle = getOmemoService().getOmemoStoreBackend().packOmemoBundle(getOwnDevice());
|
||||||
|
OmemoService.publishBundle(connection(), getOwnDevice(), bundle);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -920,10 +799,10 @@ public final class OmemoManager extends Manager {
|
||||||
// Remove listeners to avoid them getting added twice
|
// Remove listeners to avoid them getting added twice
|
||||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||||
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
pepManager.removePEPListener(deviceListUpdateListener);
|
//pepManager.removePEPListener(deviceListUpdateListener);
|
||||||
|
|
||||||
// Add listeners
|
// Add listeners
|
||||||
pepManager.addPEPListener(deviceListUpdateListener);
|
//pepManager.addPEPListener(deviceListUpdateListener);
|
||||||
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
|
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
|
||||||
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
}
|
}
|
||||||
|
@ -932,7 +811,7 @@ public final class OmemoManager extends Manager {
|
||||||
* Remove active stanza listeners needed for OMEMO.
|
* Remove active stanza listeners needed for OMEMO.
|
||||||
*/
|
*/
|
||||||
public void stopListeners() {
|
public void stopListeners() {
|
||||||
PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
//PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
||||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||||
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
}
|
}
|
||||||
|
@ -956,90 +835,6 @@ public final class OmemoManager extends Manager {
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
private final PEPListener deviceListUpdateListener = new PEPListener() {
|
|
||||||
|
|
||||||
LoggedInOmemoManager managerGuard = null;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
|
|
||||||
|
|
||||||
if (managerGuard == null) {
|
|
||||||
try {
|
|
||||||
managerGuard = new LoggedInOmemoManager(OmemoManager.this);
|
|
||||||
} catch (SmackException.NotLoggedInException e) {
|
|
||||||
throw new AssertionError(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (ExtensionElement items : event.getExtensions()) {
|
|
||||||
if (!(items instanceof ItemsExtension)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (NamedElement item : ((ItemsExtension) items).getItems()) {
|
|
||||||
if (!(item instanceof PayloadItem<?>)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
PayloadItem<?> payloadItem = (PayloadItem<?>) item;
|
|
||||||
|
|
||||||
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement_VAxolotl)) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Device List <list>
|
|
||||||
OmemoDeviceListElement_VAxolotl omemoDeviceListElement =
|
|
||||||
(OmemoDeviceListElement_VAxolotl) payloadItem.getPayload();
|
|
||||||
Integer ourDeviceId = getDeviceId();
|
|
||||||
|
|
||||||
getOmemoService().getOmemoStoreBackend()
|
|
||||||
.mergeCachedDeviceList(managerGuard.get().getOwnDevice(), from, omemoDeviceListElement);
|
|
||||||
|
|
||||||
if (from == null) {
|
|
||||||
// Unknown sender, no more work to do.
|
|
||||||
// TODO: This DOES happen for some reason. Figure out when...
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!from.equals(getOwnJid())) {
|
|
||||||
// Not our deviceList, so nothing more to do
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (omemoDeviceListElement.getDeviceIds().contains(ourDeviceId)) {
|
|
||||||
// We are on the list. Nothing more to do
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Our deviceList and we are not on it! We don't want to miss all the action!!!
|
|
||||||
LOGGER.log(Level.INFO, "Our deviceId was not on the list!");
|
|
||||||
Set<Integer> deviceListIds = omemoDeviceListElement.copyDeviceIds();
|
|
||||||
|
|
||||||
// enroll at the deviceList
|
|
||||||
deviceListIds.add(ourDeviceId);
|
|
||||||
final OmemoDeviceListElement_VAxolotl newOmemoDeviceListElement =
|
|
||||||
new OmemoDeviceListElement_VAxolotl(deviceListIds);
|
|
||||||
|
|
||||||
// PEPListener is a synchronous listener.
|
|
||||||
// Avoid any deadlocks by using an async task to update the device list.
|
|
||||||
Async.go(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
try {
|
|
||||||
OmemoService.publishDeviceIds(managerGuard, newOmemoDeviceListElement);
|
|
||||||
}
|
|
||||||
catch (SmackException | InterruptedException | XMPPException.XMPPErrorException e) {
|
|
||||||
// TODO: It might be dangerous NOT to retry publishing our deviceId
|
|
||||||
LOGGER.log(Level.SEVERE, "Could not publish our device list after an update " +
|
|
||||||
"without our id was received: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
|
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
|
||||||
@Override
|
@Override
|
||||||
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
|
|
@ -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();
|
int keyId = omemoManager.getDeviceId();
|
||||||
byte[] unpackedKey = null;
|
byte[] unpackedKey = null;
|
||||||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||||
// CHECKSTYLE: OFF
|
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
||||||
// CHECKSTYLE: ON
|
|
||||||
|
|
||||||
// Find key with our ID.
|
// Find key with our ID.
|
||||||
for (OmemoKeyElement k : keys) {
|
for (OmemoKeyElement k : keys) {
|
||||||
|
|
File diff suppressed because it is too large
Load diff
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.element.OmemoDeviceListElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
|
||||||
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
||||||
|
@ -81,12 +82,12 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
|
|
||||||
// Lookup local cached device list
|
// Lookup local cached device list
|
||||||
BareJid ownJid = userDevice.getJid();
|
BareJid ownJid = userDevice.getJid();
|
||||||
CachedDeviceList cachedDeviceList;
|
OmemoCachedDeviceList cachedDeviceList;
|
||||||
|
|
||||||
cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);
|
cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);
|
||||||
|
|
||||||
if (cachedDeviceList == null) {
|
if (cachedDeviceList == null) {
|
||||||
cachedDeviceList = new CachedDeviceList();
|
cachedDeviceList = new OmemoCachedDeviceList();
|
||||||
}
|
}
|
||||||
// Does the list already contain that id?
|
// Does the list already contain that id?
|
||||||
return !cachedDeviceList.contains(id);
|
return !cachedDeviceList.contains(id);
|
||||||
|
@ -99,11 +100,11 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @param contact Contact we received the list from.
|
* @param contact Contact we received the list from.
|
||||||
* @param list List we received.
|
* @param list List we received.
|
||||||
*/
|
*/
|
||||||
CachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
|
OmemoCachedDeviceList mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
|
||||||
CachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
|
OmemoCachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
|
||||||
|
|
||||||
if (cached == null) {
|
if (cached == null) {
|
||||||
cached = new CachedDeviceList();
|
cached = new OmemoCachedDeviceList();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (list != null) {
|
if (list != null) {
|
||||||
|
@ -166,8 +167,6 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Pack a OmemoBundleElement containing our key material.
|
* Pack a OmemoBundleElement containing our key material.
|
||||||
* If we used up n preKeys since we last published our bundle, generate n new preKeys and add them to the bundle.
|
|
||||||
* We should always publish PRE_KEY_COUNT_PER_BUNDLE keys.
|
|
||||||
*
|
*
|
||||||
* @param userDevice our OmemoDevice.
|
* @param userDevice our OmemoDevice.
|
||||||
* @return OmemoBundleElement
|
* @return OmemoBundleElement
|
||||||
|
@ -176,8 +175,6 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice)
|
OmemoBundleElement_VAxolotl packOmemoBundle(OmemoDevice userDevice)
|
||||||
throws CorruptedOmemoKeyException
|
throws CorruptedOmemoKeyException
|
||||||
{
|
{
|
||||||
createMissingKeys(userDevice);
|
|
||||||
|
|
||||||
int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice);
|
int currentSignedPreKeyId = loadCurrentOmemoSignedPreKeyId(userDevice);
|
||||||
T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKeys(userDevice).get(currentSignedPreKeyId);
|
T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKeys(userDevice).get(currentSignedPreKeyId);
|
||||||
|
|
||||||
|
@ -190,7 +187,12 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createMissingKeys(OmemoDevice userDevice)
|
/**
|
||||||
|
* Replenish our supply of keys. If we are missing any type of keys, generate them fresh.
|
||||||
|
* @param userDevice
|
||||||
|
* @throws CorruptedOmemoKeyException
|
||||||
|
*/
|
||||||
|
public void replenishKeys(OmemoDevice userDevice)
|
||||||
throws CorruptedOmemoKeyException
|
throws CorruptedOmemoKeyException
|
||||||
{
|
{
|
||||||
T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice);
|
T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice);
|
||||||
|
@ -483,14 +485,14 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @param contact contact we want to get the deviceList of
|
* @param contact contact we want to get the deviceList of
|
||||||
* @return CachedDeviceList of the contact
|
* @return CachedDeviceList of the contact
|
||||||
*/
|
*/
|
||||||
public abstract CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact);
|
public abstract OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load a list of deviceIds from our own devices.
|
* Load a list of deviceIds from our own devices.
|
||||||
* @param userDevice
|
* @param userDevice
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) {
|
public OmemoCachedDeviceList loadCachedDeviceList(OmemoDevice userDevice) {
|
||||||
return loadCachedDeviceList(userDevice, userDevice.getJid());
|
return loadCachedDeviceList(userDevice, userDevice.getJid());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -504,7 +506,7 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
*/
|
*/
|
||||||
public abstract void storeCachedDeviceList(OmemoDevice userDevice,
|
public abstract void storeCachedDeviceList(OmemoDevice userDevice,
|
||||||
BareJid contact,
|
BareJid contact,
|
||||||
CachedDeviceList contactsDeviceList);
|
OmemoCachedDeviceList contactsDeviceList);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions.
|
* Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions.
|
||||||
|
@ -537,6 +539,24 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair));
|
return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the fingerprint of the identityKey belonging to contactsDevice.
|
||||||
|
*
|
||||||
|
* @param userDevice our OmemoDevice.
|
||||||
|
* @param contactsDevice OmemoDevice we want to have the fingerprint for.
|
||||||
|
* @return fingerprint of the userDevices IdentityKey.
|
||||||
|
* @throws CorruptedOmemoKeyException if the IdentityKey is corrupted.
|
||||||
|
* @throws NoIdentityKeyException if no IdentityKey for contactsDevice has been found locally.
|
||||||
|
*/
|
||||||
|
public OmemoFingerprint getFingerprint(OmemoDevice userDevice, OmemoDevice contactsDevice)
|
||||||
|
throws CorruptedOmemoKeyException, NoIdentityKeyException {
|
||||||
|
T_IdKey identityKey = loadOmemoIdentityKey(userDevice, contactsDevice);
|
||||||
|
if (identityKey == null) {
|
||||||
|
throw new NoIdentityKeyException(contactsDevice);
|
||||||
|
}
|
||||||
|
return keyUtil().getFingerprintOfIdentityKey(identityKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the fingerprint of the given devices announced identityKey.
|
* Return the fingerprint of the given devices announced identityKey.
|
||||||
*
|
*
|
||||||
|
@ -545,7 +565,7 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws CannotEstablishOmemoSessionException if we cannot establish a session
|
* @throws CannotEstablishOmemoSessionException if we cannot establish a session
|
||||||
* @return fingerprint of the identityKey
|
* @return fingerprint of the identityKey
|
||||||
*/
|
*/
|
||||||
public OmemoFingerprint getFingerprint(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
|
public OmemoFingerprint getFingerprintAndMaybeBuildSession(OmemoManager.LoggedInOmemoManager managerGuard, OmemoDevice contactsDevice)
|
||||||
throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException
|
throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException
|
||||||
{
|
{
|
||||||
OmemoManager omemoManager = managerGuard.get();
|
OmemoManager omemoManager = managerGuard.get();
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.util.Set;
|
||||||
import org.jivesoftware.smack.packet.ExtensionElement;
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.util.Objects;
|
import org.jivesoftware.smack.util.Objects;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A OMEMO device list update containing the IDs of all active devices of a contact.
|
* A OMEMO device list update containing the IDs of all active devices of a contact.
|
||||||
|
@ -45,6 +46,10 @@ public abstract class OmemoDeviceListElement implements ExtensionElement {
|
||||||
this.deviceIds = Collections.unmodifiableSet(deviceIds);
|
this.deviceIds = Collections.unmodifiableSet(deviceIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OmemoDeviceListElement(OmemoCachedDeviceList cachedList) {
|
||||||
|
this.deviceIds = Collections.unmodifiableSet(cachedList.getActiveDevices());
|
||||||
|
}
|
||||||
|
|
||||||
public Set<Integer> getDeviceIds() {
|
public Set<Integer> getDeviceIds() {
|
||||||
return deviceIds;
|
return deviceIds;
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_
|
||||||
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The OMEMO device list element with the legacy Axolotl namespace.
|
* The OMEMO device list element with the legacy Axolotl namespace.
|
||||||
*
|
*
|
||||||
|
@ -31,6 +33,10 @@ public class OmemoDeviceListElement_VAxolotl extends OmemoDeviceListElement {
|
||||||
super(deviceIds);
|
super(deviceIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public OmemoDeviceListElement_VAxolotl(OmemoCachedDeviceList cachedList) {
|
||||||
|
super(cachedList);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return OMEMO_NAMESPACE_V_AXOLOTL;
|
return OMEMO_NAMESPACE_V_AXOLOTL;
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package org.jivesoftware.smackx.omemo.element;
|
package org.jivesoftware.smackx.omemo.element;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
import org.jivesoftware.smack.util.XmlStringBuilder;
|
import org.jivesoftware.smack.util.XmlStringBuilder;
|
||||||
|
@ -33,10 +34,10 @@ public abstract class OmemoHeaderElement implements NamedElement {
|
||||||
public static final String ATTR_IV = "iv";
|
public static final String ATTR_IV = "iv";
|
||||||
|
|
||||||
private final int sid;
|
private final int sid;
|
||||||
private final ArrayList<OmemoKeyElement> keys;
|
private final List<OmemoKeyElement> keys;
|
||||||
private final byte[] iv;
|
private final byte[] iv;
|
||||||
|
|
||||||
public OmemoHeaderElement(int sid, ArrayList<OmemoKeyElement> keys, byte[] iv) {
|
public OmemoHeaderElement(int sid, List<OmemoKeyElement> keys, byte[] iv) {
|
||||||
this.sid = sid;
|
this.sid = sid;
|
||||||
this.keys = keys;
|
this.keys = keys;
|
||||||
this.iv = iv;
|
this.iv = iv;
|
||||||
|
@ -79,4 +80,4 @@ public abstract class OmemoHeaderElement implements NamedElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,11 +16,11 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.element;
|
package org.jivesoftware.smackx.omemo.element;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.List;
|
||||||
|
|
||||||
public class OmemoHeaderElement_VAxolotl extends OmemoHeaderElement {
|
public class OmemoHeaderElement_VAxolotl extends OmemoHeaderElement {
|
||||||
|
|
||||||
public OmemoHeaderElement_VAxolotl(int sid, ArrayList<OmemoKeyElement> keys, byte[] iv) {
|
public OmemoHeaderElement_VAxolotl(int sid, List<OmemoKeyElement> keys, byte[] iv) {
|
||||||
super(sid, keys, iv);
|
super(sid, keys, iv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,4 +65,4 @@ public class OmemoKeyElement implements NamedElement {
|
||||||
sb.closeElement(this);
|
sb.closeElement(this);
|
||||||
return sb;
|
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, HashMap<OmemoDevice, Throwable>> failures = new HashMap<>();
|
||||||
private final HashMap<BareJid, ArrayList<OmemoDevice>> successes = new HashMap<>();
|
private final HashMap<BareJid, ArrayList<OmemoDevice>> successes = new HashMap<>();
|
||||||
|
|
||||||
public CannotEstablishOmemoSessionException() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) {
|
public CannotEstablishOmemoSessionException(OmemoDevice failed, Throwable reason) {
|
||||||
super();
|
super();
|
||||||
getFailsOfContact(failed.getJid()).put(failed, reason);
|
getFailsOfContact(failed.getJid()).put(failed, reason);
|
||||||
|
|
|
@ -16,6 +16,10 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.exceptions;
|
package org.jivesoftware.smackx.omemo.exceptions;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception gets thrown when some cryptographic function failed.
|
* Exception gets thrown when some cryptographic function failed.
|
||||||
*
|
*
|
||||||
|
@ -25,11 +29,18 @@ public class CryptoFailedException extends Exception {
|
||||||
|
|
||||||
private static final long serialVersionUID = 3466888654338119924L;
|
private static final long serialVersionUID = 3466888654338119924L;
|
||||||
|
|
||||||
|
private final ArrayList<Exception> exceptions = new ArrayList<>();
|
||||||
|
|
||||||
public CryptoFailedException(String message) {
|
public CryptoFailedException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoFailedException(Exception e) {
|
public CryptoFailedException(Exception e) {
|
||||||
super(e);
|
super(e);
|
||||||
|
exceptions.add(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<Exception> getExceptions() {
|
||||||
|
return Collections.unmodifiableList(exceptions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,25 +16,18 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.exceptions;
|
package org.jivesoftware.smackx.omemo.exceptions;
|
||||||
|
|
||||||
import java.io.IOException;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
public class MultipleIOException extends IOException {
|
|
||||||
|
|
||||||
|
public class NoIdentityKeyException extends Exception {
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
private final ArrayList<IOException> exceptions = new ArrayList<>();
|
private final OmemoDevice device;
|
||||||
|
|
||||||
public MultipleIOException(IOException... exceptions) {
|
public NoIdentityKeyException(OmemoDevice device) {
|
||||||
this.exceptions.addAll(Arrays.asList(exceptions));
|
this.device = device;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void addException(IOException e) {
|
public OmemoDevice getDevice() {
|
||||||
exceptions.add(e);
|
return device;
|
||||||
}
|
|
||||||
|
|
||||||
public ArrayList<IOException> getExceptions() {
|
|
||||||
return exceptions;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.exceptions;
|
package org.jivesoftware.smackx.omemo.exceptions;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
|
@ -34,6 +35,11 @@ public class UndecidedOmemoIdentityException extends Exception {
|
||||||
this.devices.add(contact);
|
this.devices.add(contact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public UndecidedOmemoIdentityException(Collection<OmemoDevice> devices) {
|
||||||
|
super();
|
||||||
|
this.devices.addAll(devices);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the HashSet of undecided devices.
|
* Return the HashSet of undecided devices.
|
||||||
*
|
*
|
||||||
|
|
|
@ -20,8 +20,9 @@ import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exception that gets thrown when we try to decrypt a message which contains an identityKey that differs from the one
|
* Exception that gets thrown when we try to en-/decrypt a message for an untrusted contact.
|
||||||
* we previously trusted.
|
* This might either be because the user actively untrusted a device, or we receive a message from a contact
|
||||||
|
* which contains an identityKey that differs from the one the user trusted.
|
||||||
*/
|
*/
|
||||||
public class UntrustedOmemoIdentityException extends Exception {
|
public class UntrustedOmemoIdentityException extends Exception {
|
||||||
|
|
||||||
|
@ -30,7 +31,8 @@ public class UntrustedOmemoIdentityException extends Exception {
|
||||||
private final OmemoFingerprint trustedKey, untrustedKey;
|
private final OmemoFingerprint trustedKey, untrustedKey;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor for when we receive a message with an identityKey different from the one we trusted.
|
||||||
|
*
|
||||||
* @param device device which sent the message.
|
* @param device device which sent the message.
|
||||||
* @param fpTrusted fingerprint of the identityKey we previously had and trusted.
|
* @param fpTrusted fingerprint of the identityKey we previously had and trusted.
|
||||||
* @param fpUntrusted fingerprint of the new key which is untrusted.
|
* @param fpUntrusted fingerprint of the new key which is untrusted.
|
||||||
|
@ -42,6 +44,16 @@ public class UntrustedOmemoIdentityException extends Exception {
|
||||||
this.untrustedKey = fpUntrusted;
|
this.untrustedKey = fpUntrusted;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for when encryption fails because the user untrusted a recipients device.
|
||||||
|
*
|
||||||
|
* @param device device the user wants to encrypt for, but which has been marked as untrusted.
|
||||||
|
* @param untrustedKey fingerprint of that device.
|
||||||
|
*/
|
||||||
|
public UntrustedOmemoIdentityException(OmemoDevice device, OmemoFingerprint untrustedKey) {
|
||||||
|
this(device, null, untrustedKey);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the device which sent the message.
|
* Return the device which sent the message.
|
||||||
* @return omemoDevice.
|
* @return omemoDevice.
|
||||||
|
@ -52,6 +64,7 @@ public class UntrustedOmemoIdentityException extends Exception {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Return the fingerprint of the key we expected.
|
* Return the fingerprint of the key we expected.
|
||||||
|
* This might return null in case this exception got thrown during encryption process.
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public OmemoFingerprint getTrustedFingerprint() {
|
public OmemoFingerprint getTrustedFingerprint() {
|
||||||
|
|
|
@ -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
|
* @author Paul Schaub
|
||||||
*/
|
*/
|
||||||
public class CachedDeviceList implements Serializable {
|
public class OmemoCachedDeviceList implements Serializable {
|
||||||
private static final long serialVersionUID = 3153579238321261203L;
|
private static final long serialVersionUID = 3153579238321261203L;
|
||||||
|
|
||||||
private final Set<Integer> activeDevices;
|
private final Set<Integer> activeDevices;
|
||||||
private final Set<Integer> inactiveDevices;
|
private final Set<Integer> inactiveDevices;
|
||||||
|
|
||||||
public CachedDeviceList() {
|
public OmemoCachedDeviceList() {
|
||||||
this.activeDevices = new HashSet<>();
|
this.activeDevices = new HashSet<>();
|
||||||
this.inactiveDevices = new HashSet<>();
|
this.inactiveDevices = new HashSet<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
|
public OmemoCachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
|
||||||
this();
|
this();
|
||||||
this.activeDevices.addAll(activeDevices);
|
this.activeDevices.addAll(activeDevices);
|
||||||
this.inactiveDevices.addAll(inactiveDevices);
|
this.inactiveDevices.addAll(inactiveDevices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CachedDeviceList(CachedDeviceList original) {
|
public OmemoCachedDeviceList(OmemoCachedDeviceList original) {
|
||||||
this(original.getActiveDevices(), original.getInactiveDevices());
|
this(original.getActiveDevices(), original.getInactiveDevices());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,6 +109,11 @@ public class CachedDeviceList implements Serializable {
|
||||||
inactiveDevices.remove(deviceId);
|
inactiveDevices.remove(deviceId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void addInactiveDevice(int deviceId) {
|
||||||
|
activeDevices.remove(deviceId);
|
||||||
|
inactiveDevices.add(deviceId);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true if deviceId is either in the list of active or inactive devices.
|
* Returns true if deviceId is either in the list of active or inactive devices.
|
||||||
*
|
*
|
|
@ -16,6 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.internal;
|
package org.jivesoftware.smackx.omemo.internal;
|
||||||
|
|
||||||
|
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,4 +76,12 @@ public class OmemoDevice {
|
||||||
i = jid.hashCode() + deviceId;
|
i = jid.hashCode() + deviceId;
|
||||||
return i.hashCode();
|
return i.hashCode();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the name of the PubSub {@link org.jivesoftware.smackx.pubsub.LeafNode} of this device.
|
||||||
|
* @return node name.
|
||||||
|
*/
|
||||||
|
public String getBundleNodeName() {
|
||||||
|
return OmemoConstants.PEP_NODE_BUNDLE_FROM_DEVICE_ID(getDeviceId());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,21 +37,21 @@ import javax.crypto.SecretKey;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
import javax.crypto.spec.SecretKeySpec;
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoRatchet;
|
import org.jivesoftware.smackx.omemo.OmemoRatchet;
|
||||||
|
import org.jivesoftware.smackx.omemo.OmemoService;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoHeaderElement_VAxolotl;
|
import org.jivesoftware.smackx.omemo.element.OmemoHeaderElement_VAxolotl;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
||||||
|
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
|
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -69,22 +69,27 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
* @author Paul Schaub
|
* @author Paul Schaub
|
||||||
*/
|
*/
|
||||||
public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||||
private final OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet;
|
|
||||||
private final OmemoManager.LoggedInOmemoManager managerGuard;
|
|
||||||
|
|
||||||
private byte[] messageKey = generateKey();
|
private final OmemoDevice userDevice;
|
||||||
private byte[] initializationVector = generateIv();
|
private final OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet;
|
||||||
|
private final TrustCallback trustCallback;
|
||||||
|
|
||||||
|
private byte[] messageKey;
|
||||||
|
private final byte[] initializationVector;
|
||||||
|
|
||||||
private byte[] ciphertextMessage;
|
private byte[] ciphertextMessage;
|
||||||
private final ArrayList<OmemoKeyElement> keys = new ArrayList<>();
|
private final ArrayList<OmemoKeyElement> keys = new ArrayList<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a OmemoMessageBuilder.
|
* Create an OmemoMessageBuilder.
|
||||||
|
*
|
||||||
|
* @param userDevice our OmemoDevice
|
||||||
|
* @param callback trustCallback for querying trust decisions
|
||||||
|
* @param ratchet our OmemoRatchet
|
||||||
|
* @param aesKey aes message key used for message encryption
|
||||||
|
* @param iv initialization vector used for message encryption
|
||||||
|
* @param message message we want to send
|
||||||
*
|
*
|
||||||
* @param managerGuard OmemoManager of our device.
|
|
||||||
* @param ratchet OmemoRatchet.
|
|
||||||
* @param aesKey AES key that will be transported to the recipient. This is used eg. to encrypt the body.
|
|
||||||
* @param iv IV
|
|
||||||
* @throws NoSuchPaddingException
|
* @throws NoSuchPaddingException
|
||||||
* @throws BadPaddingException
|
* @throws BadPaddingException
|
||||||
* @throws InvalidKeyException
|
* @throws InvalidKeyException
|
||||||
|
@ -94,24 +99,31 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
* @throws InvalidAlgorithmParameterException
|
* @throws InvalidAlgorithmParameterException
|
||||||
*/
|
*/
|
||||||
public OmemoMessageBuilder(OmemoManager.LoggedInOmemoManager managerGuard,
|
public OmemoMessageBuilder(OmemoDevice userDevice,
|
||||||
|
TrustCallback callback,
|
||||||
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
|
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
|
||||||
byte[] aesKey, byte[] iv)
|
byte[] aesKey,
|
||||||
|
byte[] iv,
|
||||||
|
String message)
|
||||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException,
|
||||||
IllegalBlockSizeException,
|
IllegalBlockSizeException,
|
||||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||||
this.managerGuard = managerGuard;
|
this.userDevice = userDevice;
|
||||||
|
this.trustCallback = callback;
|
||||||
this.ratchet = ratchet;
|
this.ratchet = ratchet;
|
||||||
this.messageKey = aesKey;
|
this.messageKey = aesKey;
|
||||||
this.initializationVector = iv;
|
this.initializationVector = iv;
|
||||||
|
setMessage(message);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new OmemoMessageBuilder with random IV and AES key.
|
* Create an OmemoMessageBuilder.
|
||||||
|
*
|
||||||
|
* @param userDevice our OmemoDevice
|
||||||
|
* @param callback trustCallback for querying trust decisions
|
||||||
|
* @param ratchet our OmemoRatchet
|
||||||
|
* @param message message we want to send
|
||||||
*
|
*
|
||||||
* @param managerGuard omemoManager of our device.
|
|
||||||
* @param ratchet omemoSessionManager.
|
|
||||||
* @param message Messages body.
|
|
||||||
* @throws NoSuchPaddingException
|
* @throws NoSuchPaddingException
|
||||||
* @throws BadPaddingException
|
* @throws BadPaddingException
|
||||||
* @throws InvalidKeyException
|
* @throws InvalidKeyException
|
||||||
|
@ -121,31 +133,32 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @throws NoSuchProviderException
|
* @throws NoSuchProviderException
|
||||||
* @throws InvalidAlgorithmParameterException
|
* @throws InvalidAlgorithmParameterException
|
||||||
*/
|
*/
|
||||||
public OmemoMessageBuilder(OmemoManager.LoggedInOmemoManager managerGuard,
|
public OmemoMessageBuilder(OmemoDevice userDevice,
|
||||||
|
TrustCallback callback,
|
||||||
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
|
OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> ratchet,
|
||||||
String message)
|
String message)
|
||||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
||||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||||
this.managerGuard = managerGuard;
|
this(userDevice, callback, ratchet, generateKey(KEYTYPE, KEYLENGTH), generateIv(), message);
|
||||||
this.ratchet = ratchet;
|
|
||||||
this.setMessage(message);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create an AES messageKey and use it to encrypt the message.
|
* Encrypt the message with the aes key.
|
||||||
* Optionally append the Auth Tag of the encrypted message to the messageKey afterwards.
|
* Move the AuthTag from the end of the cipherText to the end of the messageKey afterwards.
|
||||||
|
* This prevents an attacker which compromised one recipient device to switch out the cipherText for other recipients.
|
||||||
|
* @see <a href="https://conversations.im/omemo/audit.pdf">OMEMO security audit</a>.
|
||||||
*
|
*
|
||||||
* @param message content of the message
|
* @param message plaintext message
|
||||||
* @throws NoSuchPaddingException When no Cipher could be instantiated.
|
* @throws NoSuchPaddingException
|
||||||
* @throws NoSuchAlgorithmException when no Cipher could be instantiated.
|
* @throws NoSuchAlgorithmException
|
||||||
* @throws NoSuchProviderException when BouncyCastle could not be found.
|
* @throws NoSuchProviderException
|
||||||
* @throws InvalidAlgorithmParameterException when the Cipher could not be initialized
|
* @throws InvalidAlgorithmParameterException
|
||||||
* @throws InvalidKeyException when the generated key is invalid
|
* @throws InvalidKeyException
|
||||||
* @throws UnsupportedEncodingException when UTF8 is unavailable
|
* @throws UnsupportedEncodingException
|
||||||
* @throws BadPaddingException when cipher.doFinal gets wrong padding
|
* @throws BadPaddingException
|
||||||
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size.
|
* @throws IllegalBlockSizeException
|
||||||
*/
|
*/
|
||||||
public void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
|
private void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
|
||||||
if (message == null) {
|
if (message == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -165,56 +178,70 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
byte[] clearKeyWithAuthTag = new byte[messageKey.length + 16];
|
byte[] clearKeyWithAuthTag = new byte[messageKey.length + 16];
|
||||||
byte[] cipherTextWithoutAuthTag = new byte[ciphertext.length - 16];
|
byte[] cipherTextWithoutAuthTag = new byte[ciphertext.length - 16];
|
||||||
|
|
||||||
System.arraycopy(messageKey, 0, clearKeyWithAuthTag, 0, 16);
|
moveAuthTag(messageKey, ciphertext, clearKeyWithAuthTag, cipherTextWithoutAuthTag);
|
||||||
System.arraycopy(ciphertext, 0, cipherTextWithoutAuthTag, 0, cipherTextWithoutAuthTag.length);
|
|
||||||
System.arraycopy(ciphertext, ciphertext.length - 16, clearKeyWithAuthTag, 16, 16);
|
|
||||||
|
|
||||||
ciphertextMessage = cipherTextWithoutAuthTag;
|
ciphertextMessage = cipherTextWithoutAuthTag;
|
||||||
messageKey = clearKeyWithAuthTag;
|
messageKey = clearKeyWithAuthTag;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new recipient device to the message.
|
* Move the auth tag from the end of the cipherText to the messageKey.
|
||||||
*
|
*
|
||||||
* @param device recipient device
|
* @param messageKey source messageKey without authTag
|
||||||
* @throws CryptoFailedException when encrypting the messageKey fails
|
* @param cipherText source cipherText with authTag
|
||||||
* @throws UndecidedOmemoIdentityException
|
* @param messageKeyWithAuthTag destination messageKey with authTag
|
||||||
* @throws CorruptedOmemoKeyException
|
* @param cipherTextWithoutAuthTag destination cipherText without authTag
|
||||||
*/
|
*/
|
||||||
public void addRecipient(OmemoDevice device)
|
static void moveAuthTag(byte[] messageKey,
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
byte[] cipherText,
|
||||||
CannotEstablishOmemoSessionException {
|
byte[] messageKeyWithAuthTag,
|
||||||
addRecipient(device, false);
|
byte[] cipherTextWithoutAuthTag) {
|
||||||
|
// Check dimensions of arrays
|
||||||
|
if (messageKeyWithAuthTag.length != messageKey.length + 16) {
|
||||||
|
throw new IllegalArgumentException("Length of messageKeyWithAuthTag must be length of messageKey + " +
|
||||||
|
"length of AuthTag (16)");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cipherTextWithoutAuthTag.length != cipherText.length - 16) {
|
||||||
|
throw new IllegalArgumentException("Length of cipherTextWithoutAuthTag must be length of cipherText " +
|
||||||
|
"- length of AuthTag (16)");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move auth tag from cipherText to messageKey
|
||||||
|
System.arraycopy(messageKey, 0, messageKeyWithAuthTag, 0, 16);
|
||||||
|
System.arraycopy(cipherText, 0, cipherTextWithoutAuthTag, 0, cipherTextWithoutAuthTag.length);
|
||||||
|
System.arraycopy(cipherText, cipherText.length - 16, messageKeyWithAuthTag, 16, 16);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add a new recipient device to the message.
|
* Add a new recipient device to the message.
|
||||||
* @param contactsDevice recipient device
|
*
|
||||||
* @param ignoreTrust ignore current trust state? Useful for keyTransportMessages that are sent to repair a session
|
* @param contactsDevice device of the recipient
|
||||||
* @throws CryptoFailedException
|
* @throws NoIdentityKeyException if we have no identityKey of that device. Can be fixed by fetching and
|
||||||
* @throws UndecidedOmemoIdentityException
|
* processing the devices bundle.
|
||||||
* @throws CorruptedOmemoKeyException
|
* @throws CorruptedOmemoKeyException if the identityKey of that device is corrupted.
|
||||||
|
* @throws UndecidedOmemoIdentityException if the user hasn't yet decided whether to trust that device or not.
|
||||||
|
* @throws UntrustedOmemoIdentityException if the user has decided not to trust that device.
|
||||||
*/
|
*/
|
||||||
public void addRecipient(OmemoDevice contactsDevice, boolean ignoreTrust) throws
|
public void addRecipient(OmemoDevice contactsDevice)
|
||||||
CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
throws NoIdentityKeyException, CorruptedOmemoKeyException, UndecidedOmemoIdentityException,
|
||||||
CannotEstablishOmemoSessionException {
|
UntrustedOmemoIdentityException {
|
||||||
|
|
||||||
OmemoFingerprint fingerprint;
|
OmemoFingerprint fingerprint;
|
||||||
try {
|
fingerprint = OmemoService.getInstance().getOmemoStoreBackend().getFingerprint(userDevice, contactsDevice);
|
||||||
fingerprint = managerGuard.get().getFingerprint(contactsDevice);
|
|
||||||
} catch (SmackException.NotLoggedInException e) {
|
|
||||||
throw new AssertionError("This should never happen.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!ignoreTrust && !managerGuard.get().isDecidedOmemoIdentity(contactsDevice, fingerprint)) {
|
switch (trustCallback.getTrust(contactsDevice, fingerprint)) {
|
||||||
// Warn user of undecided device
|
|
||||||
throw new UndecidedOmemoIdentityException(contactsDevice);
|
case undecided:
|
||||||
}
|
throw new UndecidedOmemoIdentityException(contactsDevice);
|
||||||
|
|
||||||
|
case trusted:
|
||||||
|
CiphertextTuple encryptedKey = ratchet.doubleRatchetEncrypt(contactsDevice, messageKey);
|
||||||
|
keys.add(new OmemoKeyElement(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
|
||||||
|
|
||||||
|
case untrusted:
|
||||||
|
throw new UntrustedOmemoIdentityException(contactsDevice, fingerprint);
|
||||||
|
|
||||||
if (ignoreTrust || managerGuard.get().isTrustedOmemoIdentity(contactsDevice, fingerprint)) {
|
|
||||||
// Encrypt key and save to header
|
|
||||||
CiphertextTuple encryptedKey = ratchet.doubleRatchetEncrypt(contactsDevice, messageKey);
|
|
||||||
keys.add(new OmemoKeyElement(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,7 +252,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
*/
|
*/
|
||||||
public OmemoElement finish() {
|
public OmemoElement finish() {
|
||||||
OmemoHeaderElement_VAxolotl header = new OmemoHeaderElement_VAxolotl(
|
OmemoHeaderElement_VAxolotl header = new OmemoHeaderElement_VAxolotl(
|
||||||
managerGuard.get().getDeviceId(),
|
userDevice.getDeviceId(),
|
||||||
keys,
|
keys,
|
||||||
initializationVector
|
initializationVector
|
||||||
);
|
);
|
||||||
|
@ -238,9 +265,9 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
* @return new AES key
|
* @return new AES key
|
||||||
* @throws NoSuchAlgorithmException
|
* @throws NoSuchAlgorithmException
|
||||||
*/
|
*/
|
||||||
public static byte[] generateKey() throws NoSuchAlgorithmException {
|
public static byte[] generateKey(String keyType, int keyLength) throws NoSuchAlgorithmException {
|
||||||
KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
|
KeyGenerator generator = KeyGenerator.getInstance(keyType);
|
||||||
generator.init(KEYLENGTH);
|
generator.init(keyLength);
|
||||||
return generator.generateKey().getEncoded();
|
return generator.generateKey().getEncoded();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -255,12 +282,4 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
||||||
random.nextBytes(iv);
|
random.nextBytes(iv);
|
||||||
return iv;
|
return iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getCiphertextMessage() {
|
|
||||||
return ciphertextMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public byte[] getMessageKey() {
|
|
||||||
return messageKey;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
@ -23,7 +23,7 @@ import static org.junit.Assert.assertTrue;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ public class DeviceListTest {
|
||||||
*/
|
*/
|
||||||
@Test
|
@Test
|
||||||
public void mergeDeviceListsTest() {
|
public void mergeDeviceListsTest() {
|
||||||
CachedDeviceList cached = new CachedDeviceList();
|
OmemoCachedDeviceList cached = new OmemoCachedDeviceList();
|
||||||
assertNotNull(cached.getActiveDevices());
|
assertNotNull(cached.getActiveDevices());
|
||||||
assertNotNull(cached.getInactiveDevices());
|
assertNotNull(cached.getInactiveDevices());
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.fail;
|
import static junit.framework.TestCase.fail;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertNotSame;
|
import static junit.framework.TestCase.assertNotSame;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertNotNull;
|
import static junit.framework.TestCase.assertNotNull;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
|
@ -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
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertFalse;
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
@ -31,10 +31,8 @@ import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.FileBasedOmemoStore;
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
|
|
||||||
|
@ -273,7 +271,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
|
||||||
public void loadStoreCachedDeviceList() throws IOException {
|
public void loadStoreCachedDeviceList() throws IOException {
|
||||||
Integer[] active = new Integer[] {1,5,999,10};
|
Integer[] active = new Integer[] {1,5,999,10};
|
||||||
Integer[] inactive = new Integer[] {6,7,8};
|
Integer[] inactive = new Integer[] {6,7,8};
|
||||||
CachedDeviceList before = new CachedDeviceList(
|
OmemoCachedDeviceList before = new OmemoCachedDeviceList(
|
||||||
new HashSet<>(Arrays.asList(active)),
|
new HashSet<>(Arrays.asList(active)),
|
||||||
new HashSet<>(Arrays.asList(inactive)));
|
new HashSet<>(Arrays.asList(inactive)));
|
||||||
|
|
||||||
|
@ -281,7 +279,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
|
||||||
store.loadCachedDeviceList(alice, bob.getJid()));
|
store.loadCachedDeviceList(alice, bob.getJid()));
|
||||||
|
|
||||||
store.storeCachedDeviceList(alice, bob.getJid(), before);
|
store.storeCachedDeviceList(alice, bob.getJid(), before);
|
||||||
CachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid());
|
OmemoCachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid());
|
||||||
assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0);
|
assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0);
|
||||||
|
|
||||||
assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size());
|
assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size());
|
||||||
|
@ -298,7 +296,7 @@ public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey
|
||||||
assertTrue(after.getAllDevices().contains(i));
|
assertTrue(after.getAllDevices().contains(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
store.storeCachedDeviceList(alice, bob.getJid(), new CachedDeviceList());
|
store.storeCachedDeviceList(alice, bob.getJid(), new OmemoCachedDeviceList());
|
||||||
assertEquals("DeviceList must be empty after overwriting it with empty list.", 0,
|
assertEquals("DeviceList must be empty after overwriting it with empty list.", 0,
|
||||||
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
|
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
|
||||||
}
|
}
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import static junit.framework.TestCase.assertEquals;
|
import static junit.framework.TestCase.assertEquals;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
|
@ -14,7 +14,7 @@
|
||||||
* See the License for the specific language governing permissions and
|
* See the License for the specific language governing permissions and
|
||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smack.omemo.util;
|
package org.jivesoftware.smackx.omemo.util;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
|
|
@ -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