mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-12 18:49:39 +02:00
Reimplement encryption, solve compiler errors
This commit is contained in:
parent
e5cdd89edc
commit
50f8a4f1fe
20 changed files with 497 additions and 234 deletions
|
@ -29,6 +29,7 @@ import java.util.Random;
|
|||
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;
|
||||
|
@ -38,6 +39,7 @@ 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;
|
||||
|
@ -52,22 +54,26 @@ import org.jivesoftware.smackx.muc.MultiUserChat;
|
|||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||
import org.jivesoftware.smackx.muc.RoomInfo;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
||||
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.UndecidedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
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;
|
||||
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;
|
||||
|
||||
|
@ -245,7 +251,7 @@ public final class OmemoManager extends Manager {
|
|||
*
|
||||
* @param finishedCallback callback that gets called once the manager is initialized.
|
||||
*/
|
||||
public void initializeAsync(final FinishedCallback finishedCallback) {
|
||||
public void initializeAsync(final InitializationFinishedCallback finishedCallback) {
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
@ -287,8 +293,8 @@ public final class OmemoManager extends Manager {
|
|||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public OmemoMessage.Sent encrypt(BareJid recipient, String message)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException,
|
||||
InterruptedException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
|
@ -314,8 +320,8 @@ public final class OmemoManager extends Manager {
|
|||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException,
|
||||
InterruptedException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
|
@ -346,9 +352,9 @@ public final class OmemoManager extends Manager {
|
|||
* with any of their devices.
|
||||
*/
|
||||
public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
|
||||
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
|
||||
throws UndecidedOmemoIdentityException, CryptoFailedException,
|
||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
|
||||
SmackException.NoResponseException, NoOmemoSupportException,
|
||||
SmackException.NotLoggedInException
|
||||
{
|
||||
synchronized (LOCK) {
|
||||
|
@ -722,6 +728,10 @@ public final class OmemoManager extends Manager {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the deviceId of the manager to nDeviceId.
|
||||
* @param nDeviceId new deviceId
|
||||
*/
|
||||
void setDeviceId(int nDeviceId) {
|
||||
synchronized (LOCK) {
|
||||
// Move this instance inside the HashMaps
|
||||
|
@ -735,64 +745,58 @@ public final class OmemoManager extends Manager {
|
|||
/**
|
||||
* Notify all registered OmemoMessageListeners about a received OmemoMessage.
|
||||
*
|
||||
* @param decryptedBody decrypted Body element of the message
|
||||
* @param encryptedMessage unmodified message as it was received
|
||||
* @param wrappingMessage message that wrapped the incoming message
|
||||
* @param messageInformation information about the messages encryption (used identityKey, carbon...)
|
||||
* @param stanza original stanza
|
||||
* @param decryptedMessage decrypted OmemoMessage.
|
||||
*/
|
||||
void notifyOmemoMessageReceived(String decryptedBody,
|
||||
Message encryptedMessage,
|
||||
Message wrappingMessage,
|
||||
OmemoMessageInformation messageInformation)
|
||||
void notifyOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage)
|
||||
{
|
||||
for (OmemoMessageListener l : omemoMessageListeners) {
|
||||
l.onOmemoMessageReceived(decryptedBody, encryptedMessage, wrappingMessage, messageInformation);
|
||||
l.onOmemoMessageReceived(stanza, decryptedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
void notifyOmemoKeyTransportMessageReceived(CipherAndAuthTag cipherAndAuthTag,
|
||||
Message transportingMessage,
|
||||
Message wrappingMessage,
|
||||
OmemoMessageInformation information)
|
||||
/**
|
||||
* Notify all registered OmemoMessageListeners about a received OMEMO KeyTransportMessage.
|
||||
*
|
||||
* @param stanza original stanza.
|
||||
* @param decryptedKeyTransportMessage decrypted KeyTransportMessage.
|
||||
*/
|
||||
void notifyOmemoKeyTransportMessageReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage)
|
||||
{
|
||||
for (OmemoMessageListener l : omemoMessageListeners) {
|
||||
l.onOmemoKeyTransportReceived(cipherAndAuthTag, transportingMessage, wrappingMessage, information);
|
||||
l.onOmemoKeyTransportReceived(stanza, decryptedKeyTransportMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify all registered OmemoMucMessageListeners of an incoming OmemoMessageElement in a MUC.
|
||||
*
|
||||
* @param muc MultiUserChat the message was received in
|
||||
* @param from BareJid of the user that sent the message
|
||||
* @param decryptedBody decrypted body
|
||||
* @param message original message with encrypted content
|
||||
* @param wrappingMessage wrapping message (in case of carbon copy)
|
||||
* @param omemoInformation information about the encryption of the message
|
||||
* @param muc MultiUserChat the message was received in.
|
||||
* @param stanza Original Stanza.
|
||||
* @param decryptedMessage Decryped OmemoMessage.
|
||||
*/
|
||||
void notifyOmemoMucMessageReceived(MultiUserChat muc,
|
||||
BareJid from,
|
||||
String decryptedBody,
|
||||
Message message,
|
||||
Message wrappingMessage,
|
||||
OmemoMessageInformation omemoInformation)
|
||||
Stanza stanza,
|
||||
OmemoMessage.Received decryptedMessage)
|
||||
{
|
||||
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
||||
l.onOmemoMucMessageReceived(muc, from, decryptedBody, message,
|
||||
wrappingMessage, omemoInformation);
|
||||
l.onOmemoMucMessageReceived(muc, stanza, decryptedMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Notify registered OmemoMucMessageReceived listeners about KeyTransportMessages sent in a MUC.
|
||||
*
|
||||
* @param muc MultiUserChat in which the message was sent
|
||||
* @param stanza original stanza
|
||||
* @param decryptedKeyTransportMessage decrypted OMEMO KeyTransportElement
|
||||
*/
|
||||
void notifyOmemoMucKeyTransportMessageReceived(MultiUserChat muc,
|
||||
BareJid from,
|
||||
CipherAndAuthTag cipherAndAuthTag,
|
||||
Message transportingMessage,
|
||||
Message wrappingMessage,
|
||||
OmemoMessageInformation messageInformation)
|
||||
Stanza stanza,
|
||||
OmemoMessage.Received decryptedKeyTransportMessage)
|
||||
{
|
||||
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
||||
l.onOmemoKeyTransportReceived(muc, from, cipherAndAuthTag,
|
||||
transportingMessage, wrappingMessage, messageInformation);
|
||||
l.onOmemoKeyTransportReceived(muc, stanza, decryptedKeyTransportMessage);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -808,10 +812,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);
|
||||
}
|
||||
|
@ -820,7 +824,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);
|
||||
}
|
||||
|
@ -844,6 +848,9 @@ public final class OmemoManager extends Manager {
|
|||
return service;
|
||||
}
|
||||
|
||||
/**
|
||||
* StanzaListener that listens for incoming Stanzas which contain OMEMO elements.
|
||||
*/
|
||||
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
|
||||
@Override
|
||||
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
||||
|
@ -856,6 +863,9 @@ public final class OmemoManager extends Manager {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements.
|
||||
*/
|
||||
private final CarbonCopyReceivedListener internalOmemoCarbonCopyListener = new CarbonCopyReceivedListener() {
|
||||
@Override
|
||||
public void onCarbonCopyReceived(CarbonExtension.Direction direction,
|
||||
|
@ -872,6 +882,64 @@ public final class OmemoManager extends Manager {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* PEPListener that listens for OMEMO deviceList updates.
|
||||
*/
|
||||
private final PEPListener deviceListUpdateListener = new PEPListener() {
|
||||
@Override
|
||||
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
|
||||
|
||||
// Unknown sender, no more work to do.
|
||||
if (from == null) {
|
||||
// TODO: This DOES happen for some reason. Figure out when...
|
||||
return;
|
||||
}
|
||||
|
||||
for (ExtensionElement items : event.getExtensions()) {
|
||||
if (!(items instanceof ItemsExtension)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
for (ExtensionElement item : ((ItemsExtension) items).getItems()) {
|
||||
if (!(item instanceof PayloadItem<?>)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
PayloadItem<?> payloadItem = (PayloadItem<?>) item;
|
||||
|
||||
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Device List <list>
|
||||
OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload();
|
||||
getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, receivedDeviceList);
|
||||
|
||||
if (!from.asBareJid().equals(getOwnJid())) {
|
||||
continue;
|
||||
}
|
||||
|
||||
OmemoCachedDeviceList deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice());
|
||||
final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList);
|
||||
|
||||
if (!newDeviceList.equals(receivedDeviceList)) {
|
||||
Async.go(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
OmemoService.publishDeviceList(connection(), newDeviceList);
|
||||
} catch (InterruptedException | XMPPException.XMPPErrorException |
|
||||
SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* StanzaFilter that filters messages containing a OMEMO element.
|
||||
*/
|
||||
|
@ -882,6 +950,9 @@ public final class OmemoManager extends Manager {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Guard class which ensures that the wrapped OmemoManager knows its BareJid.
|
||||
*/
|
||||
public static class LoggedInOmemoManager {
|
||||
|
||||
private final OmemoManager manager;
|
||||
|
@ -909,13 +980,23 @@ public final class OmemoManager extends Manager {
|
|||
}
|
||||
}
|
||||
|
||||
public interface FinishedCallback {
|
||||
/**
|
||||
* Callback which can be used to get notified, when the OmemoManager finished initializing.
|
||||
*/
|
||||
public interface InitializationFinishedCallback {
|
||||
|
||||
void initializationFinished(OmemoManager manager);
|
||||
|
||||
void initializationFailed(Exception cause);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the bareJid of the user from the authenticated XMPP connection.
|
||||
* If our deviceId is unknown, use the bareJid to look up deviceIds available in the omemoStore.
|
||||
* If there are ids available, choose the smallest one. Otherwise generate a random deviceId.
|
||||
*
|
||||
* @param manager OmemoManager
|
||||
*/
|
||||
private static void initBareJidAndDeviceId(OmemoManager manager) {
|
||||
if (!manager.getConnection().isAuthenticated()) {
|
||||
throw new IllegalStateException("Connection MUST be authenticated.");
|
||||
|
|
|
@ -34,21 +34,32 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
|||
public class OmemoMessage {
|
||||
|
||||
private final OmemoElement element;
|
||||
private final byte[] messageKey, iv;
|
||||
|
||||
OmemoMessage(OmemoElement element) {
|
||||
OmemoMessage(OmemoElement element, byte[] key, byte[] iv) {
|
||||
this.element = element;
|
||||
this.messageKey = key;
|
||||
this.iv = iv;
|
||||
}
|
||||
|
||||
public OmemoElement getElement() {
|
||||
return element;
|
||||
}
|
||||
|
||||
public byte[] getKey() {
|
||||
return messageKey.clone();
|
||||
}
|
||||
|
||||
public byte[] getIv() {
|
||||
return iv.clone();
|
||||
}
|
||||
|
||||
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);
|
||||
Sent(OmemoElement element, byte[] key, byte[] iv, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
|
||||
super(element, key, iv);
|
||||
this.intendedDevices.addAll(intendedDevices);
|
||||
this.skippedDevices.putAll(skippedDevices);
|
||||
}
|
||||
|
@ -86,13 +97,15 @@ public class OmemoMessage {
|
|||
private final OmemoFingerprint sendersFingerprint;
|
||||
private final OmemoDevice senderDevice;
|
||||
private final CARBON carbon;
|
||||
private final boolean preKeyMessage;
|
||||
|
||||
Received(OmemoElement element, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon) {
|
||||
super(element);
|
||||
Received(OmemoElement element, byte[] key, byte[] iv, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon, boolean preKeyMessage) {
|
||||
super(element, key, iv);
|
||||
this.message = message;
|
||||
this.sendersFingerprint = sendersFingerprint;
|
||||
this.senderDevice = senderDevice;
|
||||
this.carbon = carbon;
|
||||
this.preKeyMessage = preKeyMessage;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
|
@ -115,6 +128,10 @@ public class OmemoMessage {
|
|||
public CARBON getCarbon() {
|
||||
return carbon;
|
||||
}
|
||||
|
||||
boolean isPreKeyMessage() {
|
||||
return preKeyMessage;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
|||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
|
||||
|
@ -43,16 +42,41 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
protected final OmemoManager omemoManager;
|
||||
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param omemoManager omemoManager
|
||||
* @param store omemoStore
|
||||
*/
|
||||
public OmemoRatchet(OmemoManager omemoManager,
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store) {
|
||||
this.omemoManager = omemoManager;
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt a double-ratchet-encrypted message key.
|
||||
*
|
||||
* @param sender sender of the message.
|
||||
* @param encryptedKey key encrypted with the ratchet of the sender.
|
||||
* @return decrypted message key.
|
||||
*
|
||||
* @throws CorruptedOmemoKeyException
|
||||
* @throws NoRawSessionException
|
||||
* @throws CryptoFailedException
|
||||
* @throws UntrustedOmemoIdentityException
|
||||
*/
|
||||
public abstract byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey)
|
||||
throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException,
|
||||
UntrustedOmemoIdentityException;
|
||||
|
||||
/**
|
||||
* Encrypt a messageKey with the double ratchet session of the recipient.
|
||||
*
|
||||
* @param recipient recipient of the message.
|
||||
* @param messageKey key we want to encrypt.
|
||||
* @return encrypted message key.
|
||||
*/
|
||||
public abstract CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey);
|
||||
|
||||
/**
|
||||
|
@ -70,11 +94,14 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
||||
|
||||
boolean preKey = false;
|
||||
|
||||
// Find key with our ID.
|
||||
for (OmemoKeyElement k : keys) {
|
||||
if (k.getId() == keyId) {
|
||||
try {
|
||||
unpackedKey = doubleRatchetDecrypt(sender, k.getData());
|
||||
preKey = k.isPreKey();
|
||||
break;
|
||||
} catch (CryptoFailedException e) {
|
||||
// There might be multiple keys with our id, but we can only decrypt one.
|
||||
|
@ -114,7 +141,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
+ unpackedKey.length + ". Probably legacy auth tag format.");
|
||||
}
|
||||
|
||||
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag);
|
||||
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag, preKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -123,10 +150,12 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
*
|
||||
* @param element omemoElement containing a payload.
|
||||
* @param cipherAndAuthTag cipher and authentication tag.
|
||||
* @return Message containing the decrypted payload in its body.
|
||||
* @throws CryptoFailedException
|
||||
* @return decrypted plain text.
|
||||
* @throws CryptoFailedException if decryption using AES key fails.
|
||||
*/
|
||||
static Message decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag) throws CryptoFailedException {
|
||||
static String decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag)
|
||||
throws CryptoFailedException
|
||||
{
|
||||
if (!element.isMessageElement()) {
|
||||
throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
|
||||
}
|
||||
|
@ -140,9 +169,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
|
||||
try {
|
||||
String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
|
||||
Message decrypted = new Message();
|
||||
decrypted.setBody(plaintext);
|
||||
return decrypted;
|
||||
return plaintext;
|
||||
|
||||
} catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
|
||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Date;
|
|||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import javax.crypto.BadPaddingException;
|
||||
|
@ -45,17 +46,23 @@ import org.jivesoftware.smack.XMPPException;
|
|||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||
import org.jivesoftware.smackx.muc.Occupant;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl;
|
||||
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.NoRawSessionException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.StaleDeviceException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.listener.OmemoCarbonCopyStanzaReceivedListener;
|
||||
|
@ -72,6 +79,8 @@ import org.jivesoftware.smackx.pubsub.PubSubManager;
|
|||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.EntityBareJid;
|
||||
import org.jxmpp.jid.Jid;
|
||||
|
||||
/**
|
||||
* This class contains OMEMO related logic and registers listeners etc.
|
||||
|
@ -103,8 +112,8 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
*/
|
||||
private static OmemoService<?, ?, ?, ?, ?, ?, ?, ?, ?> INSTANCE;
|
||||
|
||||
protected OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
||||
protected final HashMap<OmemoManager, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>> omemoRatchets = new HashMap<>();
|
||||
private OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
||||
private final HashMap<OmemoManager, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>> omemoRatchets = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new OmemoService object. This should only happen once.
|
||||
|
@ -120,10 +129,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @throws BadPaddingException when cipher.doFinal gets wrong padding
|
||||
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size.
|
||||
*/
|
||||
protected OmemoService()
|
||||
throws NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException,
|
||||
BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException
|
||||
{
|
||||
protected OmemoService() {
|
||||
// TODO
|
||||
}
|
||||
|
||||
|
@ -178,6 +184,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
*
|
||||
* @param omemoStore store.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public void setOmemoStoreBackend(
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore) {
|
||||
if (this.omemoStore != null) {
|
||||
|
@ -342,11 +349,11 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @throws UndecidedOmemoIdentityException if the list of recipient devices contains undecided devices
|
||||
* @throws CryptoFailedException if we are lacking some crypto primitives
|
||||
*/
|
||||
OmemoMessage.Sent encrypt(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||
List<OmemoDevice> contactsDevices,
|
||||
byte[] messageKey,
|
||||
byte[] iv,
|
||||
String message)
|
||||
private OmemoMessage.Sent encrypt(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||
List<OmemoDevice> contactsDevices,
|
||||
byte[] messageKey,
|
||||
byte[] iv,
|
||||
String message)
|
||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
||||
UndecidedOmemoIdentityException, CryptoFailedException
|
||||
{
|
||||
|
@ -418,23 +425,72 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
|
||||
OmemoElement element = builder.finish();
|
||||
|
||||
return new OmemoMessage.Sent(element, contactsDevices, skippedRecipients);
|
||||
return new OmemoMessage.Sent(element, messageKey, iv, contactsDevices, skippedRecipients);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt an OMEMO message.
|
||||
* @param managerGuard authenticated OmemoManager.
|
||||
* @param senderJid BareJid of the sender.
|
||||
* @param omemoElement omemoElement.
|
||||
* @return decrypted OmemoMessage object.
|
||||
*
|
||||
* @throws CorruptedOmemoKeyException if the identityKey of the sender is damaged.
|
||||
* @throws CryptoFailedException if decryption fails.
|
||||
* @throws NoRawSessionException if we have no session with the device and it sent a normal (non-preKey) message.
|
||||
*/
|
||||
private OmemoMessage.Received decryptMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||
BareJid senderJid,
|
||||
OmemoElement omemoElement,
|
||||
OmemoMessage.CARBON carbon)
|
||||
throws CorruptedOmemoKeyException, CryptoFailedException, NoRawSessionException
|
||||
{
|
||||
OmemoManager manager = managerGuard.get();
|
||||
int senderId = omemoElement.getHeader().getSid();
|
||||
OmemoDevice senderDevice = new OmemoDevice(senderJid, senderId);
|
||||
|
||||
CipherAndAuthTag cipherAndAuthTag = getOmemoRatchet(manager)
|
||||
.retrieveMessageKeyAndAuthTag(senderDevice, omemoElement);
|
||||
|
||||
// Retrieve senders fingerprint. TODO: Find a way to do this without the store.
|
||||
OmemoFingerprint senderFingerprint;
|
||||
try {
|
||||
senderFingerprint = getOmemoStoreBackend().getFingerprint(manager.getOwnDevice(), senderDevice);
|
||||
} catch (NoIdentityKeyException e) {
|
||||
throw new AssertionError("Cannot retrieve OmemoFingerprint of sender although decryption was successful: " + e);
|
||||
}
|
||||
|
||||
if (omemoElement.isMessageElement()) {
|
||||
// Use symmetric message key to decrypt message payload.
|
||||
String plaintext = OmemoRatchet.decryptMessageElement(omemoElement, cipherAndAuthTag);
|
||||
|
||||
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||
plaintext, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||
|
||||
} else {
|
||||
// KeyTransportMessages don't require decryption of the payload.
|
||||
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||
null, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an OMEMO KeyTransportElement.
|
||||
* @see <a href="https://xmpp.org/extensions/xep-0384.html#usecases-keysend">XEP-0384: Sending a key</a>.
|
||||
*
|
||||
* @param managerGuard Initialized OmemoManager.
|
||||
* @param contactsDevices recipient devices.
|
||||
* @param key AES-Key to be transported.
|
||||
* @param iv initialization vector to be used with the key.
|
||||
* @return KeyTransportElement
|
||||
*
|
||||
* @param managerGuard
|
||||
* @param contactsDevices
|
||||
* @param key
|
||||
* @param iv
|
||||
* @return
|
||||
* @throws InterruptedException
|
||||
* @throws UndecidedOmemoIdentityException if the list of recipients contains an undecided device
|
||||
* @throws CryptoFailedException if we are lacking some cryptographic algorithms
|
||||
* @throws SmackException.NotConnectedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
OmemoMessage.Sent createKeyTransportMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||
OmemoMessage.Sent createKeyTransportElement(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||
List<OmemoDevice> contactsDevices,
|
||||
byte[] key,
|
||||
byte[] iv)
|
||||
|
@ -570,14 +626,24 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @throws SmackException.NoResponseException
|
||||
* @throws PubSubException.NotALeafNodeException
|
||||
*/
|
||||
private static void publishDeviceList(XMPPConnection connection, OmemoDeviceListElement deviceList)
|
||||
static void publishDeviceList(XMPPConnection connection, OmemoDeviceListElement deviceList)
|
||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||
SmackException.NoResponseException, PubSubException.NotALeafNodeException
|
||||
SmackException.NoResponseException
|
||||
{
|
||||
PubSubManager.getInstance(connection, connection.getUser().asBareJid())
|
||||
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param connection
|
||||
* @param userDevice
|
||||
* @throws InterruptedException
|
||||
* @throws PubSubException.NotALeafNodeException
|
||||
* @throws XMPPException.XMPPErrorException
|
||||
* @throws SmackException.NotConnectedException
|
||||
* @throws SmackException.NoResponseException
|
||||
*/
|
||||
private void refreshAndRepublishDeviceList(XMPPConnection connection, OmemoDevice userDevice)
|
||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||
SmackException.NotConnectedException, SmackException.NoResponseException
|
||||
|
@ -592,21 +658,10 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
if (publishedList == null) {
|
||||
publishedList = new OmemoDeviceListElement_VAxolotl(Collections.<Integer>emptySet());
|
||||
}
|
||||
OmemoCachedDeviceList cachedList = getOmemoStoreBackend().mergeCachedDeviceList(
|
||||
userDevice, userDevice.getJid(), publishedList);
|
||||
|
||||
getOmemoStoreBackend().mergeCachedDeviceList(userDevice, userDevice.getJid(), publishedList);
|
||||
|
||||
// Delete stale devices if allowed and necessary
|
||||
if (OmemoConfiguration.getDeleteStaleDevices()) {
|
||||
cachedList = deleteStaleDevices(userDevice);
|
||||
}
|
||||
|
||||
// Add back our device if necessary
|
||||
if (!cachedList.getActiveDevices().contains(userDevice.getDeviceId())) {
|
||||
cachedList.addDevice(userDevice.getDeviceId());
|
||||
}
|
||||
|
||||
getOmemoStoreBackend().storeCachedDeviceList(userDevice, userDevice.getJid(), cachedList);
|
||||
OmemoCachedDeviceList cachedList = cleanUpDeviceList(userDevice);
|
||||
|
||||
// Republish our deviceId if it is missing from the published list.
|
||||
if (!publishedList.getDeviceIds().equals(cachedList.getActiveDevices())) {
|
||||
|
@ -614,6 +669,33 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add our load the deviceList of the user from cache, delete stale devices if needed, add the users device
|
||||
* back if necessary, store the refurbished list in cache and return it.
|
||||
*
|
||||
* @param userDevice
|
||||
* @return
|
||||
*/
|
||||
OmemoCachedDeviceList cleanUpDeviceList(OmemoDevice userDevice) {
|
||||
OmemoCachedDeviceList cachedDeviceList;
|
||||
|
||||
// Delete stale devices if allowed and necessary
|
||||
if (OmemoConfiguration.getDeleteStaleDevices()) {
|
||||
cachedDeviceList = deleteStaleDevices(userDevice);
|
||||
} else {
|
||||
cachedDeviceList = getOmemoStoreBackend().loadCachedDeviceList(userDevice);
|
||||
}
|
||||
|
||||
|
||||
// Add back our device if necessary
|
||||
if (!cachedDeviceList.getActiveDevices().contains(userDevice.getDeviceId())) {
|
||||
cachedDeviceList.addDevice(userDevice.getDeviceId());
|
||||
}
|
||||
|
||||
getOmemoStoreBackend().storeCachedDeviceList(userDevice, userDevice.getJid(), cachedDeviceList);
|
||||
return cachedDeviceList;
|
||||
}
|
||||
|
||||
/**
|
||||
* Refresh and merge device list of contact.
|
||||
*
|
||||
|
@ -831,7 +913,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @param contactsDevice OmemoDevice of the contact.
|
||||
* @return true if userDevice has session with contactsDevice.
|
||||
*/
|
||||
boolean hasSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
private boolean hasSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
return getOmemoStoreBackend().loadRawSession(userDevice, contactsDevice) != null;
|
||||
}
|
||||
|
||||
|
@ -948,7 +1030,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @param userDevice our OmemoDevice
|
||||
* @param devices collection of OmemoDevices
|
||||
*/
|
||||
static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
||||
private static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
||||
if (devices.contains(userDevice)) {
|
||||
devices.remove(userDevice);
|
||||
}
|
||||
|
@ -996,7 +1078,74 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager omemoManager) {
|
||||
// TODO: Implement
|
||||
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) {
|
||||
OmemoManager manager = managerGuard.get();
|
||||
OmemoElement element = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
|
||||
if (element == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
OmemoMessage.Received decrypted;
|
||||
|
||||
MultiUserChat muc = getMuc(manager.getConnection(), stanza.getFrom());
|
||||
if (muc != null) {
|
||||
Occupant occupant = muc.getOccupant(stanza.getFrom().asEntityFullJidIfPossible());
|
||||
Jid occupantJid = occupant.getJid();
|
||||
|
||||
if (occupantJid == null) {
|
||||
LOGGER.log(Level.WARNING, "MUC message received, but there is no way to retrieve the senders Jid. " +
|
||||
stanza.getFrom());
|
||||
return;
|
||||
}
|
||||
|
||||
BareJid sender = occupantJid.asBareJid();
|
||||
try {
|
||||
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
||||
|
||||
if (element.isMessageElement()) {
|
||||
manager.notifyOmemoMucMessageReceived(muc, stanza, decrypted);
|
||||
} else {
|
||||
manager.notifyOmemoMucKeyTransportMessageReceived(muc, stanza, decrypted);
|
||||
}
|
||||
|
||||
} catch (CorruptedOmemoKeyException | CryptoFailedException | NoRawSessionException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
||||
}
|
||||
|
||||
} else {
|
||||
BareJid sender = stanza.getFrom().asBareJid();
|
||||
try {
|
||||
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
||||
|
||||
if (element.isMessageElement()) {
|
||||
manager.notifyOmemoMessageReceived(stanza, decrypted);
|
||||
} else {
|
||||
manager.notifyOmemoKeyTransportMessageReceived(stanza, decrypted);
|
||||
}
|
||||
} catch (CorruptedOmemoKeyException | CryptoFailedException | NoRawSessionException e) {
|
||||
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the joined MUC with EntityBareJid jid, or null if its not a room and/or not joined.
|
||||
* @param connection xmpp connection
|
||||
* @param jid jid (presumably) of the MUC
|
||||
* @return MultiUserChat or null if not a MUC.
|
||||
*/
|
||||
private static MultiUserChat getMuc(XMPPConnection connection, Jid jid) {
|
||||
EntityBareJid ebj = jid.asEntityBareJidIfPossible();
|
||||
if (ebj == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(connection);
|
||||
Set<EntityBareJid> joinedRooms = mucm.getJoinedRooms();
|
||||
if (joinedRooms.contains(ebj)) {
|
||||
return mucm.getMultiUserChat(ebj);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_
|
|||
*/
|
||||
public class OmemoElement_VAxolotl extends OmemoElement {
|
||||
|
||||
public static final String NAMESPACE = OMEMO_NAMESPACE_V_AXOLOTL;
|
||||
|
||||
/**
|
||||
* Create a new OmemoMessageElement from a header and a payload.
|
||||
*
|
||||
|
@ -37,6 +39,6 @@ public class OmemoElement_VAxolotl extends OmemoElement {
|
|||
|
||||
@Override
|
||||
public String getNamespace() {
|
||||
return OMEMO_NAMESPACE_V_AXOLOTL;
|
||||
return NAMESPACE;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,19 @@
|
|||
/**
|
||||
*
|
||||
* 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.element;
|
||||
|
||||
import org.jivesoftware.smack.packet.NamedElement;
|
||||
|
|
|
@ -78,4 +78,16 @@ public class UntrustedOmemoIdentityException extends Exception {
|
|||
public OmemoFingerprint getUntrustedFingerprint() {
|
||||
return untrustedKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
if (trustedKey != null) {
|
||||
return "Untrusted OMEMO Identity encountered:\n" +
|
||||
"Fingerprint of trusted key:\n" + trustedKey.blocksOf8Chars() + "\n" +
|
||||
"Fingerprint of untrusted key:\n" + untrustedKey.blocksOf8Chars();
|
||||
} else {
|
||||
return "Untrusted OMEMO Identity encountered:\n" +
|
||||
"Fingerprint of untrusted key:\n" + untrustedKey.blocksOf8Chars();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
|
|||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
|
@ -38,11 +37,13 @@ import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
|||
*/
|
||||
public class CipherAndAuthTag {
|
||||
private final byte[] key, iv, authTag;
|
||||
private final boolean wasPreKey;
|
||||
|
||||
public CipherAndAuthTag(byte[] key, byte[] iv, byte[] authTag) throws CryptoFailedException {
|
||||
public CipherAndAuthTag(byte[] key, byte[] iv, byte[] authTag, boolean wasPreKey) {
|
||||
this.authTag = authTag;
|
||||
this.key = key;
|
||||
this.iv = iv;
|
||||
this.wasPreKey = wasPreKey;
|
||||
}
|
||||
|
||||
public Cipher getCipher() throws CryptoFailedException {
|
||||
|
@ -82,4 +83,8 @@ public class CipherAndAuthTag {
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean wasPreKeyEncrypted() {
|
||||
return wasPreKey;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.listener;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
||||
|
||||
/**
|
||||
* Listener interface that allows implementations to receive decrypted OMEMO messages.
|
||||
|
@ -30,20 +28,16 @@ public interface OmemoMessageListener {
|
|||
/**
|
||||
* Gets called, whenever an OmemoMessage has been received and was successfully decrypted.
|
||||
*
|
||||
* @param decryptedBody Decrypted body
|
||||
* @param encryptedMessage Encrypted Message
|
||||
* @param wrappingMessage Wrapping carbon message, in case the message was a carbon copy, else null.
|
||||
* @param omemoInformation Information about the messages encryption etc.
|
||||
* @param stanza Received (encrypted) stanza.
|
||||
* @param decryptedMessage decrypted OmemoMessage.
|
||||
*/
|
||||
void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation);
|
||||
void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage);
|
||||
|
||||
/**
|
||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
||||
*
|
||||
* @param cipherAndAuthTag transported Cipher along with an optional AuthTag
|
||||
* @param message Message that contained the KeyTransport
|
||||
* @param wrappingMessage Wrapping message (eg. carbon), or null
|
||||
* @param omemoInformation Information about the messages encryption etc.
|
||||
* @param stanza received (encrypted) stanza.
|
||||
* @param decryptedKeyTransportMessage decrypted OMEMO key transport message.
|
||||
*/
|
||||
void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation);
|
||||
void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage);
|
||||
}
|
||||
|
|
|
@ -16,13 +16,9 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.listener;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
||||
|
||||
/**
|
||||
* Listener interface that allows implementations to receive decrypted OMEMO MUC messages.
|
||||
|
@ -33,24 +29,17 @@ public interface OmemoMucMessageListener {
|
|||
/**
|
||||
* Gets called whenever an OMEMO message has been received in a MultiUserChat and successfully decrypted.
|
||||
* @param muc MultiUserChat the message was sent in
|
||||
* @param from the bareJid of the sender
|
||||
* @param decryptedBody the decrypted Body of the message
|
||||
* @param message the original message with encrypted element
|
||||
* @param wrappingMessage in case of a carbon copy, this is the wrapping message
|
||||
* @param omemoInformation information about the encryption of the message
|
||||
* @param stanza Original Stanza
|
||||
* @param decryptedOmemoMessage decrypted Omemo message
|
||||
*/
|
||||
void onOmemoMucMessageReceived(MultiUserChat muc, BareJid from, String decryptedBody, Message message,
|
||||
Message wrappingMessage, OmemoMessageInformation omemoInformation);
|
||||
void onOmemoMucMessageReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedOmemoMessage);
|
||||
|
||||
/**
|
||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
||||
*
|
||||
* @param muc MultiUserChat the message was sent in
|
||||
* @param from bareJid of the sender
|
||||
* @param cipherAndAuthTag transportedKey along with an optional authTag
|
||||
* @param message Message that contained the KeyTransport
|
||||
* @param wrappingMessage Wrapping message (eg. carbon), or null
|
||||
* @param omemoInformation Information about the messages encryption etc.
|
||||
* @param muc MultiUserChat the message was sent in
|
||||
* @param stanza original stanza
|
||||
* @param decryptedKeyTransportElement decrypted KeyTransportMessage
|
||||
*/
|
||||
void onOmemoKeyTransportReceived(MultiUserChat muc, BareJid from, CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation);
|
||||
void onOmemoKeyTransportReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedKeyTransportElement);
|
||||
}
|
||||
|
|
|
@ -21,8 +21,6 @@ import static org.junit.Assert.assertNull;
|
|||
|
||||
import java.io.File;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoConfiguration;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
import org.junit.Test;
|
||||
|
||||
|
|
|
@ -61,11 +61,12 @@ public class WrapperObjectsTest {
|
|||
byte[] iv = OmemoMessageBuilder.generateIv();
|
||||
byte[] authTag = OmemoMessageBuilder.generateIv();
|
||||
|
||||
CipherAndAuthTag cat = new CipherAndAuthTag(key, iv, authTag);
|
||||
CipherAndAuthTag cat = new CipherAndAuthTag(key, iv, authTag, true);
|
||||
|
||||
assertNotNull(cat.getCipher());
|
||||
assertArrayEquals(key, cat.getKey());
|
||||
assertArrayEquals(iv, cat.getIv());
|
||||
assertArrayEquals(authTag, cat.getAuthTag());
|
||||
assertTrue(cat.wasPreKeyEncrypted());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue