Temp commit

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,79 @@
/**
*
* Copyright the original author or authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jivesoftware.smackx.omemo.util;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import java.util.Random;
import org.jivesoftware.smack.test.util.SmackTestSuite;
import org.junit.Test;
public class OmemoMessageBuilderTest extends SmackTestSuite {
private static final byte[] messageKey = new byte[16];
private static final byte[] cipherTextWithAuthTag = new byte[35 + 16];
public OmemoMessageBuilderTest() {
Random random = new Random();
random.nextBytes(messageKey);
random.nextBytes(cipherTextWithAuthTag);
}
@Test
public void testMoveAuthTag() {
// Extract authTag for testing purposes
byte[] authTag = new byte[16];
System.arraycopy(cipherTextWithAuthTag, 35, authTag, 0, 16);
byte[] messageKeyWithAuthTag = new byte[16 + 16];
byte[] cipherTextWithoutAuthTag = new byte[35];
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, messageKeyWithAuthTag, cipherTextWithoutAuthTag);
// Check if first n - 16 bytes of cipherText got copied over to cipherTextWithoutAuthTag correctly
byte[] checkCipherText = new byte[35];
System.arraycopy(cipherTextWithAuthTag, 0, checkCipherText, 0, 35);
assertTrue(Arrays.equals(checkCipherText, cipherTextWithoutAuthTag));
byte[] checkMessageKey = new byte[16];
System.arraycopy(messageKeyWithAuthTag, 0, checkMessageKey, 0, 16);
assertTrue(Arrays.equals(checkMessageKey, messageKey));
byte[] checkAuthTag = new byte[16];
System.arraycopy(messageKeyWithAuthTag, 16, checkAuthTag, 0, 16);
assertTrue(Arrays.equals(checkAuthTag, authTag));
}
@Test(expected = IllegalArgumentException.class)
public void testCheckIllegalMessageKeyWithAuthTagLength() {
byte[] illegalMessageKey = new byte[16 + 15]; // too short
byte[] cipherTextWithoutAuthTag = new byte[35]; // ok
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, illegalMessageKey, cipherTextWithoutAuthTag);
}
@Test(expected = IllegalArgumentException.class)
public void testCheckIllegalCipherTextWithoutAuthTagLength() {
byte[] messageKeyWithAuthTag = new byte[16 + 16]; // ok
byte[] illegalCipherTextWithoutAuthTag = new byte[39]; // too long
OmemoMessageBuilder.moveAuthTag(messageKey, cipherTextWithAuthTag, messageKeyWithAuthTag, illegalCipherTextWithoutAuthTag);
}
}