getUntrustedDevices() {
+ return this.devices;
+ }
+
+ /**
+ * Add all untrusted devices of another Exception to this Exceptions HashSet of untrusted devices.
+ *
+ * @param other other Exception
+ */
+ public void join(UndecidedOmemoIdentityException other) {
+ this.devices.addAll(other.getUntrustedDevices());
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/package-info.java
new file mode 100644
index 000000000..007ed78ac
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/exceptions/package-info.java
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Exceptions.
+ *
+ * @author Paul Schaub
+ * @see XEP-0384: OMEMO
+ */
+package org.jivesoftware.smackx.omemo.exceptions;
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CachedDeviceList.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CachedDeviceList.java
new file mode 100644
index 000000000..41662e1ff
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CachedDeviceList.java
@@ -0,0 +1,124 @@
+/**
+ *
+ * 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 java.io.Serializable;
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * This class is used to represent device lists of contacts.
+ * There are active devices (a set of device ids, which was published with the last device list update)
+ * and inactive devices (set of devices that once were active, but are not included in recent list updates).
+ * Both kinds are cached by the client. When a device that was active in the last update is not included in
+ * a new update, it becomes an inactive device. Vice versa, inactive devices can also become active again, by
+ * being included in the latest device list update.
+ *
+ * The client ensures, that his own device id is on the list of active devices, as soon as he gets online.
+ *
+ * @author Paul Schaub
+ */
+public class CachedDeviceList implements Serializable {
+ private static final long serialVersionUID = 3153579238321261203L;
+
+ private final Set activeDevices;
+ private final Set inactiveDevices;
+
+ public CachedDeviceList() {
+ this.activeDevices = new HashSet<>();
+ this.inactiveDevices = new HashSet<>();
+ }
+
+ /**
+ * Returns all active devices.
+ * Active devices are all devices that were in the latest DeviceList update.
+ *
+ * @return active devices
+ */
+ public Set getActiveDevices() {
+ return activeDevices;
+ }
+
+ /**
+ * Return all inactive devices.
+ * Inactive devices are devices which were in a past DeviceList update once, but were not included in
+ * the latest update.
+ *
+ * @return inactive devices
+ */
+ public Set getInactiveDevices() {
+ return inactiveDevices;
+ }
+
+ /**
+ * Returns an OmemoDeviceListElement containing all devices (active and inactive).
+ *
+ * @return all devices
+ */
+ public Set getAllDevices() {
+ Set all = new HashSet<>();
+ all.addAll(activeDevices);
+ all.addAll(inactiveDevices);
+ return all;
+ }
+
+ /**
+ * Merge a device list update into the CachedDeviceList.
+ * The source code should be self explanatory.
+ *
+ * @param deviceListUpdate received device list update
+ */
+ public void merge(Set deviceListUpdate) {
+ inactiveDevices.addAll(activeDevices);
+ activeDevices.clear();
+ activeDevices.addAll(deviceListUpdate);
+ inactiveDevices.removeAll(activeDevices);
+ }
+
+ /**
+ * Add a device to the list of active devices.
+ *
+ * @param deviceId deviceId that will be added
+ */
+ public void addDevice(int deviceId) {
+ activeDevices.add(deviceId);
+ }
+
+ /**
+ * Returns true if deviceId is either in the list of active or inactive devices.
+ *
+ * @param deviceId id
+ * @return true or false
+ */
+ public boolean contains(int deviceId) {
+ return activeDevices.contains(deviceId) || inactiveDevices.contains(deviceId);
+ }
+
+ @Override
+ public String toString() {
+ String out = "active: [";
+ for (int id : activeDevices) {
+ out += id + " ";
+ }
+ out += "] inacitve: [";
+ for (int id : inactiveDevices) {
+ out += id + " ";
+ }
+ out += "]";
+ return out;
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java
new file mode 100644
index 000000000..6d5e49a3d
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CipherAndAuthTag.java
@@ -0,0 +1,84 @@
+/**
+ *
+ * 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.smackx.omemo.exceptions.CryptoFailedException;
+
+import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+
+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;
+
+/**
+ * Encapsulate Cipher and AuthTag.
+ *
+ * @author Paul Schaub
+ */
+public class CipherAndAuthTag {
+ private final byte[] key, iv, authTag;
+
+ public CipherAndAuthTag(byte[] key, byte[] iv, byte[] authTag) throws CryptoFailedException {
+ this.authTag = authTag;
+ this.key = key;
+ this.iv = iv;
+ }
+
+ public Cipher getCipher() throws CryptoFailedException {
+
+ Cipher cipher;
+ try {
+ cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ SecretKeySpec keySpec = new SecretKeySpec(key, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(iv);
+ cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
+ } catch (NoSuchAlgorithmException | java.security.InvalidKeyException |
+ InvalidAlgorithmParameterException |
+ NoSuchPaddingException | NoSuchProviderException e) {
+ throw new CryptoFailedException(e);
+ }
+
+ return cipher;
+ }
+
+ public byte[] getAuthTag() {
+ if (authTag != null) {
+ return authTag.clone();
+ }
+ return null;
+ }
+
+ public byte[] getKey() {
+ if (key != null) {
+ return key.clone();
+ }
+ return null;
+ }
+
+ public byte[] getIv() {
+ if (iv != null) {
+ return iv.clone();
+ }
+ return null;
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CiphertextTuple.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CiphertextTuple.java
new file mode 100644
index 000000000..c94a5bc88
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/CiphertextTuple.java
@@ -0,0 +1,67 @@
+/**
+ *
+ * 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 static org.jivesoftware.smackx.omemo.element.OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE;
+
+/**
+ * Bundles a decrypted ciphertext together with information about the message type.
+ *
+ * @author Paul Schaub
+ */
+public class CiphertextTuple {
+ private final byte[] ciphertext;
+ private final int messageType;
+
+ /**
+ * Create a new CiphertextTuple.
+ *
+ * @param ciphertext ciphertext
+ * @param type type
+ */
+ public CiphertextTuple(byte[] ciphertext, int type) {
+ this.ciphertext = ciphertext;
+ this.messageType = type;
+ }
+
+ /**
+ * Return the ciphertext.
+ *
+ * @return ciphertext
+ */
+ public byte[] getCiphertext() {
+ return ciphertext;
+ }
+
+ /**
+ * Return the messageType.
+ *
+ * @return messageType
+ */
+ public int getMessageType() {
+ return this.messageType;
+ }
+
+ /**
+ * Returns true if this is a preKeyMessage.
+ *
+ * @return preKeyMessage?
+ */
+ public boolean isPreKeyMessage() {
+ return getMessageType() == TYPE_OMEMO_PREKEY_MESSAGE;
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/ClearTextMessage.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/ClearTextMessage.java
new file mode 100644
index 000000000..928e4fb5f
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/ClearTextMessage.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * 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;
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/IdentityKeyWrapper.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/IdentityKeyWrapper.java
new file mode 100644
index 000000000..c89bd33f4
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/IdentityKeyWrapper.java
@@ -0,0 +1,36 @@
+/**
+ *
+ * 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;
+
+/**
+ * Wrapper for IdentityKey objects.
+ *
+ * @author Paul Schaub
+ */
+public class IdentityKeyWrapper {
+ private final Object identityKey;
+
+ public IdentityKeyWrapper(Object wrapped) {
+ identityKey = wrapped;
+ }
+
+ public Object getIdentityKey() {
+ return identityKey;
+ }
+
+
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoDevice.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoDevice.java
new file mode 100644
index 000000000..81102ce25
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoDevice.java
@@ -0,0 +1,77 @@
+/**
+ *
+ * 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.jxmpp.jid.BareJid;
+
+/**
+ * Class that combines a BareJid and a deviceId.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoDevice {
+ private final BareJid jid;
+ private final int deviceId;
+
+ /**
+ * Create a new OmemoDevice.
+ *
+ * @param jid jid of the contact
+ * @param deviceId deviceId if the device.
+ */
+ public OmemoDevice(BareJid jid, int deviceId) {
+ this.jid = jid;
+ this.deviceId = deviceId;
+ }
+
+ /**
+ * Return the BareJid of the device owner.
+ *
+ * @return bareJid
+ */
+ public BareJid getJid() {
+ return this.jid;
+ }
+
+ /**
+ * Return the OMEMO device Id of the device.
+ *
+ * @return deviceId
+ */
+ public int getDeviceId() {
+ return this.deviceId;
+ }
+
+ @Override
+ public String toString() {
+ return jid.toString() + ":" + deviceId;
+ }
+
+ @Override
+ public boolean equals(Object other) {
+ return other instanceof OmemoDevice &&
+ ((OmemoDevice) other).getJid().equals(this.getJid()) &&
+ ((OmemoDevice) other).getDeviceId() == this.getDeviceId();
+ }
+
+ @Override
+ public int hashCode() {
+ Integer i;
+ i = jid.hashCode() + deviceId;
+ return i.hashCode();
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoMessageInformation.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoMessageInformation.java
new file mode 100644
index 000000000..ada9cc2b6
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoMessageInformation.java
@@ -0,0 +1,141 @@
+/**
+ *
+ * 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;
+
+/**
+ * Class that contains information about a decrypted message (eg. which key was used, if it was a carbon...).
+ *
+ * @author Paul Schaub
+ */
+public class OmemoMessageInformation {
+ private boolean isOmemoMessage;
+ private IdentityKeyWrapper senderIdentityKey;
+ private OmemoDevice senderDevice;
+ private CARBON carbon = CARBON.NONE;
+
+ /**
+ * Empty constructor.
+ */
+ // TOOD Move this class into smackx.omemo and make this constructor package protected. -Flow
+ public OmemoMessageInformation() {
+ }
+
+ /**
+ * Creates a new OmemoMessageInformation object. Its assumed, that this is about an OMEMO message.
+ *
+ * @param senderIdentityKey identityKey of the sender device
+ * @param senderDevice device that sent the message
+ * @param carbon Carbon type
+ */
+ public OmemoMessageInformation(IdentityKeyWrapper senderIdentityKey, OmemoDevice senderDevice, CARBON carbon) {
+ this.senderIdentityKey = senderIdentityKey;
+ this.senderDevice = senderDevice;
+ this.carbon = carbon;
+ this.isOmemoMessage = true;
+ }
+
+ /**
+ * Create a new OmemoMessageInformation.
+ *
+ * @param senderIdentityKey identityKey of the sender device
+ * @param senderDevice device that sent the message
+ * @param carbon Carbon type
+ * @param omemo is this an omemo message?
+ */
+ public OmemoMessageInformation(IdentityKeyWrapper senderIdentityKey, OmemoDevice senderDevice, CARBON carbon, boolean omemo) {
+ this(senderIdentityKey, senderDevice, carbon);
+ this.isOmemoMessage = omemo;
+ }
+
+ /**
+ * Return the sender devices identityKey.
+ *
+ * @return identityKey
+ */
+ public IdentityKeyWrapper getSenderIdentityKey() {
+ return senderIdentityKey;
+ }
+
+ /**
+ * Set the sender devices identityKey.
+ *
+ * @param senderIdentityKey identityKey
+ */
+ public void setSenderIdentityKey(IdentityKeyWrapper senderIdentityKey) {
+ this.senderIdentityKey = senderIdentityKey;
+ }
+
+ /**
+ * Return the sender device.
+ *
+ * @return sender device
+ */
+ public OmemoDevice getSenderDevice() {
+ return senderDevice;
+ }
+
+ /**
+ * Return true, if this is (was) an OMEMO message.
+ * @return true if omemo
+ */
+ public boolean isOmemoMessage() {
+ return this.isOmemoMessage;
+ }
+
+ /**
+ * Set the sender device.
+ *
+ * @param senderDevice sender device
+ */
+ public void setSenderDevice(OmemoDevice senderDevice) {
+ this.senderDevice = senderDevice;
+ }
+
+ /**
+ * Return the carbon type.
+ *
+ * @return carbon type
+ */
+ public CARBON getCarbon() {
+ return carbon;
+ }
+
+ /**
+ * Set the carbon type.
+ *
+ * @param carbon carbon type
+ */
+ public void setCarbon(CARBON carbon) {
+ this.carbon = carbon;
+ }
+
+ /**
+ * Types of Carbon Messages.
+ */
+ public enum CARBON {
+ NONE, //No carbon
+ SENT, //Sent carbon
+ RECV //Received Carbon
+ }
+
+ @Override
+ public String toString() {
+ return (senderDevice != null ? senderDevice.toString() : "") + " " + carbon;
+ }
+}
+
+
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoSession.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoSession.java
new file mode 100644
index 000000000..023728532
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/OmemoSession.java
@@ -0,0 +1,264 @@
+/**
+ *
+ * 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;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.omemo.OmemoFingerprint;
+import org.jivesoftware.smackx.omemo.OmemoManager;
+import org.jivesoftware.smackx.omemo.OmemoStore;
+import org.jivesoftware.smackx.omemo.element.OmemoElement;
+import org.jivesoftware.smackx.omemo.element.OmemoElement.OmemoHeader.Key;
+import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
+import org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException;
+import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.IllegalBlockSizeException;
+import java.io.UnsupportedEncodingException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * This class represents a OMEMO session between us and another device.
+ *
+ * @param IdentityKeyPair class
+ * @param IdentityKey class
+ * @param PreKey class
+ * @param SignedPreKey class
+ * @param Session class
+ * @param Address class
+ * @param Elliptic Curve PublicKey class
+ * @param Bundle class
+ * @param Cipher class
+ * @author Paul Schaub
+ */
+public abstract class OmemoSession {
+
+ protected final T_Ciph cipher;
+ protected final OmemoStore omemoStore;
+ protected final OmemoDevice remoteDevice;
+ protected final OmemoManager omemoManager;
+ protected T_IdKey identityKey;
+ protected int preKeyId = -1;
+
+ /**
+ * Constructor used when we establish the session.
+ *
+ * @param omemoManager OmemoManager of our device
+ * @param omemoStore OmemoStore where we want to store the session and get key information from
+ * @param remoteDevice the OmemoDevice we want to establish the session with
+ * @param identityKey identityKey of the recipient
+ */
+ public OmemoSession(OmemoManager omemoManager,
+ OmemoStore omemoStore,
+ OmemoDevice remoteDevice, T_IdKey identityKey) {
+ this(omemoManager, omemoStore, remoteDevice);
+ this.identityKey = identityKey;
+ }
+
+ /**
+ * Another constructor used when they establish the session with us.
+ *
+ * @param omemoManager OmemoManager of our device
+ * @param omemoStore OmemoStore we want to store the session and their key in
+ * @param remoteDevice identityKey of the partner
+ */
+ public OmemoSession(OmemoManager omemoManager, OmemoStore omemoStore,
+ OmemoDevice remoteDevice) {
+ this.omemoManager = omemoManager;
+ this.omemoStore = omemoStore;
+ this.remoteDevice = remoteDevice;
+ this.cipher = createCipher(remoteDevice);
+ }
+
+ /**
+ * Try to decrypt the transported message key using the double ratchet session.
+ *
+ * @param element omemoElement
+ * @param keyId our keyId
+ * @return tuple of cipher generated from the unpacked message key and the authtag
+ * @throws CryptoFailedException if decryption using the double ratchet fails
+ * @throws NoRawSessionException if we have no session, but the element was NOT a PreKeyMessage
+ */
+ public CipherAndAuthTag decryptTransportedKey(OmemoElement element, int keyId) throws CryptoFailedException,
+ NoRawSessionException {
+ byte[] unpackedKey = null;
+ List decryptExceptions = new ArrayList<>();
+ List keys = element.getHeader().getKeys();
+ // Find key with our ID.
+ for (OmemoElement.OmemoHeader.Key k : keys) {
+ if (k.getId() == keyId) {
+ try {
+ unpackedKey = decryptMessageKey(k.getData());
+ break;
+ } catch (CryptoFailedException e) {
+ // There might be multiple keys with our id, but we can only decrypt one.
+ // So we can't throw the exception, when decrypting the first duplicate which is not for us.
+ decryptExceptions.add(e);
+ }
+ }
+ }
+
+ if (unpackedKey == null) {
+ if (!decryptExceptions.isEmpty()) {
+ throw MultipleCryptoFailedException.from(decryptExceptions);
+ }
+
+ throw new CryptoFailedException("Transported key could not be decrypted, since no provided message key. Provides keys: " + keys);
+ }
+
+ byte[] messageKey = new byte[16];
+ byte[] authTag = null;
+
+ if (unpackedKey.length == 32) {
+ authTag = new byte[16];
+ //copy key part into messageKey
+ System.arraycopy(unpackedKey, 0, messageKey, 0, 16);
+ //copy tag part into authTag
+ System.arraycopy(unpackedKey, 16, authTag, 0,16);
+ } else if (element.isKeyTransportElement() && unpackedKey.length == 16) {
+ messageKey = unpackedKey;
+ } else {
+ throw new CryptoFailedException("MessageKey has wrong length: "
+ + unpackedKey.length + ". Probably legacy auth tag format.");
+ }
+
+ return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag);
+ }
+
+ /**
+ * Use the symmetric key in cipherAndAuthTag to decrypt the payload of the omemoMessage.
+ * The decrypted payload will be the body of the returned Message.
+ *
+ * @param element omemoElement containing a payload.
+ * @param cipherAndAuthTag cipher and authentication tag.
+ * @return Message containing the decrypted payload in its body.
+ * @throws CryptoFailedException
+ */
+ public static Message decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag) throws CryptoFailedException {
+ if (!element.isMessageElement()) {
+ throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
+ }
+
+ if (cipherAndAuthTag.getAuthTag() == null || cipherAndAuthTag.getAuthTag().length != 16) {
+ throw new CryptoFailedException("AuthenticationTag is null or has wrong length: "
+ + (cipherAndAuthTag.getAuthTag() == null ? "null" : cipherAndAuthTag.getAuthTag().length));
+ }
+ byte[] encryptedBody = new byte[element.getPayload().length + 16];
+ byte[] payload = element.getPayload();
+ System.arraycopy(payload, 0, encryptedBody, 0, payload.length);
+ System.arraycopy(cipherAndAuthTag.getAuthTag(), 0, encryptedBody, payload.length, 16);
+
+ try {
+ String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
+ Message decrypted = new Message();
+ decrypted.setBody(plaintext);
+ return decrypted;
+
+ } catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
+ throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
+ + e.getMessage());
+ }
+ }
+
+ /**
+ * Try to decrypt the message.
+ * First decrypt the message key using our session with the sender.
+ * Second use the decrypted key to decrypt the message.
+ * The decrypted content of the 'encrypted'-element becomes the body of the clear text message.
+ *
+ * @param element OmemoElement
+ * @param keyId the key we want to decrypt (usually our own device id)
+ * @return message as plaintext
+ * @throws CryptoFailedException
+ * @throws NoRawSessionException
+ */
+ // TODO find solution for what we actually want to decrypt (String, Message, List...)
+ public Message decryptMessageElement(OmemoElement element, int keyId) throws CryptoFailedException, NoRawSessionException {
+ if (!element.isMessageElement()) {
+ throw new IllegalArgumentException("OmemoElement is not a messageElement!");
+ }
+
+ CipherAndAuthTag cipherAndAuthTag = decryptTransportedKey(element, keyId);
+ return decryptMessageElement(element, cipherAndAuthTag);
+ }
+
+ /**
+ * Create a new SessionCipher used to encrypt/decrypt keys. The cipher typically implements the ratchet and KDF-chains.
+ *
+ * @param contact OmemoDevice
+ * @return SessionCipher
+ */
+ public abstract T_Ciph createCipher(OmemoDevice contact);
+
+ /**
+ * Get the id of the preKey used to establish the session.
+ *
+ * @return id
+ */
+ public int getPreKeyId() {
+ return this.preKeyId;
+ }
+
+ /**
+ * Encrypt a message key for the recipient. This key can be deciphered by the recipient with its corresponding
+ * session cipher. The key is then used to decipher the message.
+ *
+ * @param messageKey serialized key to encrypt
+ * @return A CiphertextTuple containing the ciphertext and the messageType
+ * @throws CryptoFailedException
+ */
+ public abstract CiphertextTuple encryptMessageKey(byte[] messageKey) throws CryptoFailedException;
+
+ /**
+ * Decrypt a messageKey using our sessionCipher. We can use that key to decipher the actual message.
+ * Same as encryptMessageKey, just the other way round.
+ *
+ * @param encryptedKey encrypted key
+ * @return serialized decrypted key or null
+ * @throws CryptoFailedException when decryption fails.
+ * @throws NoRawSessionException when no session was found in the double ratchet library
+ */
+ public abstract byte[] decryptMessageKey(byte[] encryptedKey) throws CryptoFailedException, NoRawSessionException;
+
+ /**
+ * Return the identityKey of the session.
+ *
+ * @return identityKey
+ */
+ public T_IdKey getIdentityKey() {
+ return identityKey;
+ }
+
+ /**
+ * Set the identityKey of the remote device.
+ * @param identityKey identityKey
+ */
+ public void setIdentityKey(T_IdKey identityKey) {
+ this.identityKey = identityKey;
+ }
+
+ /**
+ * Return the fingerprint of the contacts identityKey.
+ *
+ * @return fingerprint or null
+ */
+ public OmemoFingerprint getFingerprint() {
+ return (this.identityKey != null ? omemoStore.keyUtil().getFingerprint(this.identityKey) : null);
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/package-info.java
new file mode 100644
index 000000000..485f603ea
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/internal/package-info.java
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Classes that are used internally to arrange objects.
+ *
+ * @author Paul Schaub
+ * @see XEP-0384: OMEMO
+ */
+package org.jivesoftware.smackx.omemo.internal;
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMessageListener.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMessageListener.java
new file mode 100644
index 000000000..ed319e043
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMessageListener.java
@@ -0,0 +1,48 @@
+/**
+ *
+ * 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.listener;
+
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
+import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
+
+/**
+ * Listener interface that allows implementations to receive decrypted OMEMO messages.
+ *
+ * @author Paul Schaub
+ */
+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.
+ */
+ void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation);
+
+ /**
+ * 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.
+ */
+ void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation);
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMucMessageListener.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMucMessageListener.java
new file mode 100644
index 000000000..019999cdd
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/OmemoMucMessageListener.java
@@ -0,0 +1,54 @@
+/**
+ *
+ * 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.listener;
+
+import org.jivesoftware.smack.packet.Message;
+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;
+
+/**
+ * Listener interface that allows implementations to receive decrypted OMEMO MUC messages.
+ * @author Paul Schaub
+ */
+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
+ */
+ void onOmemoMucMessageReceived(MultiUserChat muc, BareJid from, String decryptedBody, Message message,
+ Message wrappingMessage, OmemoMessageInformation omemoInformation);
+
+ /**
+ * 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.
+ */
+ void onOmemoKeyTransportReceived(MultiUserChat muc, BareJid from, CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation);
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/package-info.java
new file mode 100644
index 000000000..b9eeb58c7
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/listener/package-info.java
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Callbacks and listeners.
+ *
+ * @author Paul Schaub
+ * @see XEP-XXXX: OMEMO
+ */
+package org.jivesoftware.smackx.omemo.listener;
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java
new file mode 100644
index 000000000..737ea40e6
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/package-info.java
@@ -0,0 +1,26 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Classes and interfaces for OMEMO Encryption. This module consists of the
+ * XMPP logic and some abstract crypto classes that have to be implemented
+ * using concrete crypto libraries (like signal-protocol-java or olm).
+ * See smack-omemo-signal for a concrete implementation (GPL licensed).
+ *
+ * @author Paul Schaub
+ * @see XEP-0384: OMEMO
+ */
+package org.jivesoftware.smackx.omemo;
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoBundleVAxolotlProvider.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoBundleVAxolotlProvider.java
new file mode 100644
index 000000000..1de76e644
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoBundleVAxolotlProvider.java
@@ -0,0 +1,99 @@
+/**
+ *
+ * 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.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.HashMap;
+
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.BUNDLE;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.IDENTITY_KEY;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEYS;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEY_ID;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.PRE_KEY_PUB;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.SIGNED_PRE_KEY_ID;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.SIGNED_PRE_KEY_PUB;
+import static org.jivesoftware.smackx.omemo.element.OmemoBundleElement.SIGNED_PRE_KEY_SIG;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+/**
+ * Smack ExtensionProvider that parses OMEMO bundle element into OmemoBundleElement objects.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoBundleVAxolotlProvider extends ExtensionElementProvider {
+ @Override
+ public OmemoBundleVAxolotlElement parse(XmlPullParser parser, int initialDepth) throws Exception {
+ boolean stop = false;
+ boolean inPreKeys = false;
+
+ int signedPreKeyId = -1;
+ String signedPreKey = null;
+ String signedPreKeySignature = null;
+ String identityKey = null;
+ HashMap preKeys = new HashMap<>();
+
+ while (!stop) {
+ int tag = parser.next();
+ String name = parser.getName();
+ switch (tag) {
+ case START_TAG:
+ //
+ if (name.equals(SIGNED_PRE_KEY_PUB)) {
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals(SIGNED_PRE_KEY_ID)) {
+ int id = Integer.parseInt(parser.getAttributeValue(i));
+ signedPreKey = parser.nextText();
+ signedPreKeyId = id;
+ }
+ }
+ }
+ //
+ else if (name.equals(SIGNED_PRE_KEY_SIG)) {
+ signedPreKeySignature = parser.nextText();
+ }
+ //
+ else if (name.equals(IDENTITY_KEY)) {
+ identityKey = parser.nextText();
+ }
+ //
+ else if (name.equals(PRE_KEYS)) {
+ inPreKeys = true;
+ }
+ //
+ else if (inPreKeys && name.equals(PRE_KEY_PUB)) {
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals(PRE_KEY_ID)) {
+ preKeys.put(Integer.parseInt(parser.getAttributeValue(i)),
+ parser.nextText());
+ }
+ }
+ }
+ break;
+ case END_TAG:
+ if (name.equals(BUNDLE)) {
+ stop = true;
+ }
+ break;
+ }
+ }
+ return new OmemoBundleVAxolotlElement(signedPreKeyId, signedPreKey, signedPreKeySignature, identityKey, preKeys);
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoDeviceListVAxolotlProvider.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoDeviceListVAxolotlProvider.java
new file mode 100644
index 000000000..69f8cdf75
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoDeviceListVAxolotlProvider.java
@@ -0,0 +1,66 @@
+/**
+ *
+ * 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.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smackx.omemo.element.OmemoDeviceListVAxolotlElement;
+import org.xmlpull.v1.XmlPullParser;
+
+import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.DEVICE;
+import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.ID;
+import static org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement.LIST;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * Smack ExtensionProvider that parses OMEMO device list element into OmemoDeviceListElement objects.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoDeviceListVAxolotlProvider extends ExtensionElementProvider {
+
+ @Override
+ public OmemoDeviceListVAxolotlElement parse(XmlPullParser parser, int initialDepth) throws Exception {
+ Set deviceListIds = new HashSet<>();
+ boolean stop = false;
+ while (!stop) {
+ int tag = parser.next();
+ String name = parser.getName();
+ switch (tag) {
+ case START_TAG:
+ if (name.equals(DEVICE)) {
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals(ID)) {
+ Integer deviceId = Integer.parseInt(parser.getAttributeValue(i));
+ deviceListIds.add(deviceId);
+ }
+ }
+ }
+ break;
+ case END_TAG:
+ if (name.equals(LIST)) {
+ stop = true;
+ }
+ break;
+ }
+ }
+ return new OmemoDeviceListVAxolotlElement(deviceListIds);
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoVAxolotlProvider.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoVAxolotlProvider.java
new file mode 100644
index 000000000..47826e23d
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/OmemoVAxolotlProvider.java
@@ -0,0 +1,95 @@
+/**
+ *
+ * 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.provider;
+
+import org.jivesoftware.smack.provider.ExtensionElementProvider;
+import org.jivesoftware.smack.util.stringencoder.Base64;
+import org.jivesoftware.smackx.omemo.element.OmemoVAxolotlElement;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.ArrayList;
+
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.ENCRYPTED;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.HEADER;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.IV;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.KEY;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.PAYLOAD;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.PREKEY;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.RID;
+import static org.jivesoftware.smackx.omemo.element.OmemoElement.SID;
+import static org.xmlpull.v1.XmlPullParser.END_TAG;
+import static org.xmlpull.v1.XmlPullParser.START_TAG;
+
+/**
+ * Smack ExtensionProvider that parses incoming OMEMO Message element into OmemoMessageElement objects.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoVAxolotlProvider extends ExtensionElementProvider {
+
+ @Override
+ public OmemoVAxolotlElement parse(XmlPullParser parser, int initialDepth) throws Exception {
+ boolean inEncrypted = true;
+ int sid = -1;
+ ArrayList keys = new ArrayList<>();
+ byte[] iv = null;
+ byte[] payload = null;
+
+ while (inEncrypted) {
+ int tag = parser.next();
+ String name = parser.getName();
+ switch (tag) {
+ case START_TAG:
+ switch (name) {
+ case HEADER:
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals(SID)) {
+ sid = Integer.parseInt(parser.getAttributeValue(i));
+ }
+ }
+ break;
+ case KEY:
+ boolean prekey = false;
+ int rid = -1;
+ for (int i = 0; i < parser.getAttributeCount(); i++) {
+ if (parser.getAttributeName(i).equals(PREKEY)) {
+ prekey = Boolean.parseBoolean(parser.getAttributeValue(i));
+ } else if (parser.getAttributeName(i).equals(RID)) {
+ rid = Integer.parseInt(parser.getAttributeValue(i));
+ }
+ }
+ keys.add(new OmemoVAxolotlElement.OmemoHeader.Key(Base64.decode(parser.nextText()), rid, prekey));
+ break;
+ case IV:
+ iv = Base64.decode(parser.nextText());
+ break;
+ case PAYLOAD:
+ payload = Base64.decode(parser.nextText());
+ break;
+ }
+ break;
+ case END_TAG:
+ if (name.equals(ENCRYPTED)) {
+ inEncrypted = false;
+ }
+ break;
+ }
+ }
+ OmemoVAxolotlElement.OmemoHeader header = new OmemoVAxolotlElement.OmemoHeader(sid, keys, iv);
+ return new OmemoVAxolotlElement(header, payload);
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/package-info.java
new file mode 100644
index 000000000..aba66090a
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/provider/package-info.java
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Provider classes that parse OMEMO related stanzas into objects.
+ *
+ * @author Paul Schaub
+ * @see XEP-0384: OMEMO
+ */
+package org.jivesoftware.smackx.omemo.provider;
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java
new file mode 100644
index 000000000..442da7a24
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoConstants.java
@@ -0,0 +1,63 @@
+/**
+ *
+ * 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.util;
+
+/**
+ * Some constants related to OMEMO.
+ * @author Paul Schaub
+ */
+public final class OmemoConstants {
+
+ //Constants
+ /**
+ * Omemo related namespace.
+ */
+ public static final String OMEMO_NAMESPACE_V_AXOLOTL = "eu.siacs.conversations.axolotl";
+ public static final String OMEMO = "OMEMO";
+
+ //PubSub Node names
+ public static final String PEP_NODE_DEVICE_LIST = OMEMO_NAMESPACE_V_AXOLOTL + ".devicelist";
+ public static final String PEP_NODE_DEVICE_LIST_NOTIFY = PEP_NODE_DEVICE_LIST + "+notify";
+ public static final String PEP_NODE_BUNDLES = OMEMO_NAMESPACE_V_AXOLOTL + ".bundles";
+
+ /**
+ * How many preKeys do we want to publish?
+ */
+ public static final int TARGET_PRE_KEY_COUNT = 100;
+
+ /**
+ * Return the node name of the PEP node containing the device bundle of the device with device id deviceId.
+ *
+ * @param deviceId id of the device
+ * @return node name of the devices bundle node
+ */
+ public static String PEP_NODE_BUNDLE_FROM_DEVICE_ID(int deviceId) {
+ return PEP_NODE_BUNDLES + ":" + deviceId;
+ }
+
+ public static final String BODY_OMEMO_HINT = "I sent you an OMEMO encrypted message but your client doesn’t seem to support that. Find more information on https://conversations.im/omemo";
+
+ /**
+ * Information about the keys used for message encryption.
+ */
+ public static final class Crypto {
+ public static final String KEYTYPE = "AES";
+ public static final int KEYLENGTH = 128;
+ public static final String CIPHERMODE = "AES/GCM/NoPadding";
+ public static final String PROVIDER = "BC";
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java
new file mode 100644
index 000000000..5ca08109c
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoKeyUtil.java
@@ -0,0 +1,450 @@
+/**
+ *
+ * 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.util;
+
+import org.jivesoftware.smackx.omemo.OmemoFingerprint;
+import org.jivesoftware.smackx.omemo.OmemoManager;
+import org.jivesoftware.smackx.omemo.OmemoStore;
+import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
+import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
+import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
+import org.jivesoftware.smackx.omemo.internal.OmemoSession;
+import org.jxmpp.stringprep.XmppStringprepException;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * Class that is used to convert bytes to keys and vice versa.
+ *
+ * @param IdentityKeyPair class
+ * @param IdentityKey class
+ * @param PreKey class
+ * @param SignedPreKey class
+ * @param Session class
+ * @param Address class
+ * @param Elliptic Curve PublicKey class
+ * @param Bundle class
+ * @param Cipher class
+ * @author Paul Schaub
+ */
+public abstract class OmemoKeyUtil {
+ private static final Logger LOGGER = Logger.getLogger(OmemoKeyUtil.class.getName());
+
+ public final Bundle BUNDLE = new Bundle();
+
+ /**
+ * Bundle related methods.
+ */
+ public class Bundle {
+
+ /**
+ * Extract an IdentityKey from a OmemoBundleElement.
+ *
+ * @param bundle OmemoBundleElement
+ * @return identityKey
+ * @throws CorruptedOmemoKeyException if the key is damaged/malformed
+ */
+ public T_IdKey identityKey(OmemoBundleVAxolotlElement bundle) throws CorruptedOmemoKeyException {
+ return identityKeyFromBytes(bundle.getIdentityKey());
+ }
+
+ /**
+ * Extract a signedPreKey from an OmemoBundleElement.
+ *
+ * @param bundle OmemoBundleElement
+ * @return singedPreKey
+ * @throws CorruptedOmemoKeyException if the key is damaged/malformed
+ */
+ public T_ECPub signedPreKeyPublic(OmemoBundleVAxolotlElement bundle) throws CorruptedOmemoKeyException {
+ return signedPreKeyPublicFromBytes(bundle.getSignedPreKey());
+ }
+
+ /**
+ * Extract the id of the transported signedPreKey from the bundle.
+ *
+ * @param bundle OmemoBundleElement
+ * @return signedPreKeyId
+ */
+ public int signedPreKeyId(OmemoBundleVAxolotlElement bundle) {
+ return bundle.getSignedPreKeyId();
+ }
+
+ /**
+ * Extract the signature of the signedPreKey in the bundle as a byte array.
+ *
+ * @param bundle OmemoBundleElement
+ * @return signature
+ */
+ public byte[] signedPreKeySignature(OmemoBundleVAxolotlElement bundle) {
+ return bundle.getSignedPreKeySignature();
+ }
+
+ /**
+ * Extract the preKey with id 'keyId' from the bundle.
+ *
+ * @param bundle OmemoBundleElement
+ * @param keyId id of the preKey
+ * @return the preKey
+ * @throws CorruptedOmemoKeyException when the key cannot be parsed from bytes
+ */
+ public T_ECPub preKeyPublic(OmemoBundleVAxolotlElement bundle, int keyId) throws CorruptedOmemoKeyException {
+ return preKeyPublicFromBytes(bundle.getPreKey(keyId));
+ }
+
+ /**
+ * Break up the OmemoBundleElement into a list of crypto-lib specific bundles (T_PreKey).
+ * In case of the signal library, we break the OmemoBundleElement in ~100 PreKeyBundles (one for every transported
+ * preKey).
+ *
+ * @param bundle OmemoBundleElement containing multiple PreKeys
+ * @param contact Contact that the bundle belongs to
+ * @return a HashMap with one T_Bundle per preKey and the preKeyId as key
+ * @throws CorruptedOmemoKeyException when one of the keys cannot be parsed
+ */
+ public HashMap bundles(OmemoBundleVAxolotlElement bundle, OmemoDevice contact) throws CorruptedOmemoKeyException {
+ HashMap bundles = new HashMap<>();
+ for (int deviceId : bundle.getPreKeys().keySet()) {
+ try {
+ bundles.put(deviceId, bundleFromOmemoBundle(bundle, contact, deviceId));
+ } catch (CorruptedOmemoKeyException e) {
+ LOGGER.log(Level.INFO, "Cannot parse PreKeyBundle: " + e.getMessage());
+ }
+ }
+ if (bundles.size() == 0) {
+ throw new CorruptedOmemoKeyException("Bundle contained no valid preKeys.");
+ }
+ return bundles;
+ }
+ }
+
+ /**
+ * Deserialize an identityKeyPair from a byte array.
+ *
+ * @param data byte array
+ * @return IdentityKeyPair (T_IdKeyPair)
+ * @throws CorruptedOmemoKeyException if the key is damaged of malformed
+ */
+ public abstract T_IdKeyPair identityKeyPairFromBytes(byte[] data) throws CorruptedOmemoKeyException;
+
+ /**
+ * Deserialize an identityKey from a byte array.
+ *
+ * @param data byte array
+ * @return identityKey (T_IdKey)
+ * @throws CorruptedOmemoKeyException if the key is damaged or malformed
+ */
+ public abstract T_IdKey identityKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException;
+
+ /**
+ * Serialize an identityKey into bytes.
+ *
+ * @param identityKey idKey
+ * @return bytes
+ */
+ public abstract byte[] identityKeyToBytes(T_IdKey identityKey);
+
+ /**
+ * Deserialize an elliptic curve public key from bytes.
+ *
+ * @param data bytes
+ * @return elliptic curve public key (T_ECPub)
+ * @throws CorruptedOmemoKeyException if the key is damaged or malformed
+ */
+ public abstract T_ECPub ellipticCurvePublicKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException;
+
+ /**
+ * Deserialize a public preKey from bytes.
+ *
+ * @param data preKey as bytes
+ * @return preKey
+ * @throws CorruptedOmemoKeyException if the key is damaged or malformed
+ */
+ public T_ECPub preKeyPublicFromBytes(byte[] data) throws CorruptedOmemoKeyException {
+ return ellipticCurvePublicKeyFromBytes(data);
+ }
+
+ /**
+ * Serialize a preKey into a byte array.
+ *
+ * @param preKey preKey
+ * @return byte[]
+ */
+ public abstract byte[] preKeyToBytes(T_PreKey preKey);
+
+ /**
+ * Deserialize a preKey from a byte array.
+ *
+ * @param bytes byte array
+ * @return deserialized preKey
+ * @throws IOException when something goes wrong
+ */
+ public abstract T_PreKey preKeyFromBytes(byte[] bytes) throws IOException;
+
+ /**
+ * Generate 'count' new PreKeys beginning with id 'startId'.
+ * These preKeys are published and can be used by contacts to establish sessions with us.
+ *
+ * @param startId start id
+ * @param count how many keys do we want to generate
+ * @return Map of new preKeys
+ */
+ public abstract HashMap generateOmemoPreKeys(int startId, int count);
+
+ /**
+ * Generate a new signed preKey.
+ *
+ * @param identityKeyPair identityKeyPair used to sign the preKey
+ * @param signedPreKeyId id that the preKey will have
+ * @return signedPreKey
+ * @throws CorruptedOmemoKeyException when the identityKeyPair is invalid
+ */
+ public abstract T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId) throws CorruptedOmemoKeyException;
+
+
+ /**
+ * Deserialize a public signedPreKey from bytes.
+ *
+ * @param data bytes
+ * @return signedPreKey
+ * @throws CorruptedOmemoKeyException if the key is damaged or malformed
+ */
+ public T_ECPub signedPreKeyPublicFromBytes(byte[] data) throws CorruptedOmemoKeyException {
+ return ellipticCurvePublicKeyFromBytes(data);
+ }
+
+ /**
+ * Deserialize a signedPreKey from a byte array.
+ *
+ * @param data byte array
+ * @return deserialized signed preKey
+ * @throws IOException when something goes wrong
+ */
+ public abstract T_SigPreKey signedPreKeyFromBytes(byte[] data) throws IOException;
+
+ /**
+ * Serialize a signedPreKey into a byte array.
+ *
+ * @param sigPreKey signedPreKey
+ * @return byte array
+ */
+ public abstract byte[] signedPreKeyToBytes(T_SigPreKey sigPreKey);
+
+ /**
+ * Build a crypto-lib specific PreKeyBundle (T_Bundle) using a PreKey from the OmemoBundleElement 'bundle'.
+ * The PreKeyBundle will contain the identityKey, signedPreKey and signature, as well as a preKey
+ * from the OmemoBundleElement.
+ *
+ * @param bundle OmemoBundleElement
+ * @param contact Contact that the bundle belongs to
+ * @param keyId id of the preKey that will be selected from the OmemoBundleElement and that the PreKeyBundle will contain
+ * @return PreKeyBundle (T_PreKey)
+ * @throws CorruptedOmemoKeyException if some key is damaged or malformed
+ */
+ public abstract T_Bundle bundleFromOmemoBundle(OmemoBundleVAxolotlElement bundle, OmemoDevice contact, int keyId) throws CorruptedOmemoKeyException;
+
+ /**
+ * Extract the signature from a signedPreKey.
+ *
+ * @param signedPreKey signedPreKey
+ * @return signature as byte array
+ */
+ public abstract byte[] signedPreKeySignatureFromKey(T_SigPreKey signedPreKey);
+
+ /**
+ * Generate a new IdentityKeyPair. We should always have only one pair and usually keep this for a long time.
+ *
+ * @return identityKeyPair
+ */
+ public abstract T_IdKeyPair generateOmemoIdentityKeyPair();
+
+ /**
+ * return the id of the given signedPreKey.
+ *
+ * @param signedPreKey key
+ * @return id of the key
+ */
+ public abstract int signedPreKeyIdFromKey(T_SigPreKey signedPreKey);
+
+ /**
+ * serialize an identityKeyPair into bytes.
+ *
+ * @param identityKeyPair identityKeyPair
+ * @return byte array
+ */
+ public abstract byte[] identityKeyPairToBytes(T_IdKeyPair identityKeyPair);
+
+ /**
+ * Extract the public identityKey from an identityKeyPair.
+ *
+ * @param pair keyPair
+ * @return public key of the pair
+ */
+ public abstract T_IdKey identityKeyFromPair(T_IdKeyPair pair);
+
+ /**
+ * Prepare an identityKey for transport in an OmemoBundleElement (serialize it).
+ *
+ * @param identityKey identityKey that will be transported
+ * @return key as byte array
+ */
+ public abstract byte[] identityKeyForBundle(T_IdKey identityKey);
+
+ /**
+ * Prepare an elliptic curve preKey for transport in an OmemoBundleElement.
+ *
+ * @param preKey key
+ * @return key as byte array
+ */
+ public abstract byte[] preKeyPublicKeyForBundle(T_ECPub preKey);
+
+ /**
+ * Prepare a preKey for transport in an OmemoBundleElement.
+ *
+ * @param preKey preKey
+ * @return key as byte array
+ */
+ public abstract byte[] preKeyForBundle(T_PreKey preKey);
+
+ /**
+ * Prepare a whole bunche of preKeys for transport.
+ *
+ * @param preKeyHashMap HashMap of preKeys
+ * @return HashMap of byte arrays but with the same keyIds as key
+ */
+ public HashMap preKeyPublisKeysForBundle(HashMap preKeyHashMap) {
+ HashMap out = new HashMap<>();
+ for (Map.Entry e : preKeyHashMap.entrySet()) {
+ out.put(e.getKey(), preKeyForBundle(e.getValue()));
+ }
+ return out;
+ }
+
+ /**
+ * Prepare a public signedPreKey for transport in a bundle.
+ *
+ * @param signedPreKey signedPrekey
+ * @return signedPreKey as byte array
+ */
+ public abstract byte[] signedPreKeyPublicForBundle(T_SigPreKey signedPreKey);
+
+ /**
+ * Return the fingerprint of an identityKey.
+ *
+ * @param identityKey identityKey
+ * @return fingerprint of the key
+ */
+ public abstract OmemoFingerprint getFingerprint(T_IdKey identityKey);
+
+ /**
+ * Create a new crypto-specific Session object.
+ *
+ * @param omemoManager omemoManager of our device.
+ * @param omemoStore omemoStore where we can save the session, get keys from etc.
+ * @param from the device we want to create the session with.
+ * @return a new session
+ */
+ public abstract OmemoSession
+ createOmemoSession(OmemoManager omemoManager, OmemoStore omemoStore,
+ OmemoDevice from);
+
+ /**
+ * Create a new concrete OmemoSession with a contact.
+ *
+ * @param omemoManager omemoManager of our device.
+ * @param omemoStore omemoStore
+ * @param device device to establish the session with
+ * @param identityKey identityKey of the device
+ * @return concrete OmemoSession
+ */
+ public abstract OmemoSession
+ createOmemoSession(OmemoManager omemoManager, OmemoStore omemoStore,
+ OmemoDevice device, T_IdKey identityKey);
+
+ /**
+ * Deserialize a raw OMEMO Session from bytes.
+ *
+ * @param data bytes
+ * @return raw OMEMO Session
+ * @throws IOException when something goes wrong
+ */
+ public abstract T_Sess rawSessionFromBytes(byte[] data) throws IOException;
+
+ /**
+ * Serialize a raw OMEMO session into a byte array.
+ *
+ * @param session raw session
+ * @return byte array
+ */
+ public abstract byte[] rawSessionToBytes(T_Sess session);
+
+ /**
+ * Convert an OmemoDevice to a crypto-lib specific contact format.
+ *
+ * @param contact omemoContact
+ * @return crypto-lib specific contact object
+ */
+ public abstract T_Addr omemoDeviceAsAddress(OmemoDevice contact);
+
+ /**
+ * Convert a crypto-lib specific contact object into an OmemoDevice.
+ *
+ * @param address contact
+ * @return as OmemoDevice
+ * @throws XmppStringprepException if the address is not a valid BareJid
+ */
+ public abstract OmemoDevice addressAsOmemoDevice(T_Addr address) throws XmppStringprepException;
+
+ public static String prettyFingerprint(OmemoFingerprint fingerprint) {
+ return prettyFingerprint(fingerprint.toString());
+ }
+
+ /**
+ * Split the fingerprint in blocks of 8 characters with spaces between.
+ *
+ * @param ugly fingerprint as continuous string
+ * @return fingerprint with spaces for better readability
+ */
+ public static String prettyFingerprint(String ugly) {
+ if (ugly == null) return null;
+ String pretty = "";
+ for (int i = 0; i < 8; i++) {
+ if (i != 0) pretty += " ";
+ pretty += ugly.substring(8 * i, 8 * (i + 1));
+ }
+ return pretty;
+ }
+
+ /**
+ * Add integers modulo MAX_VALUE.
+ *
+ * @param value base integer
+ * @param added value that is added to the base value
+ * @return (value plus added) modulo Integer.MAX_VALUE
+ */
+ public static int addInBounds(int value, int added) {
+ int avail = Integer.MAX_VALUE - value;
+ if (avail < added) {
+ return added - avail;
+ } else {
+ return value + added;
+ }
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java
new file mode 100644
index 000000000..e682b865c
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/OmemoMessageBuilder.java
@@ -0,0 +1,252 @@
+/**
+ *
+ * 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.util;
+
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smackx.omemo.OmemoManager;
+import org.jivesoftware.smackx.omemo.OmemoStore;
+import org.jivesoftware.smackx.omemo.element.OmemoVAxolotlElement;
+import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
+import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
+import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
+import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
+import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
+import org.jivesoftware.smackx.omemo.internal.OmemoSession;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.KeyGenerator;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.io.UnsupportedEncodingException;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.SecureRandom;
+import java.util.ArrayList;
+
+import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMODE;
+import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYLENGTH;
+import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE;
+import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
+
+/**
+ * Class used to build OMEMO messages.
+ *
+ * @param IdentityKeyPair class
+ * @param IdentityKey class
+ * @param PreKey class
+ * @param SignedPreKey class
+ * @param Session class
+ * @param Address class
+ * @param Elliptic Curve PublicKey class
+ * @param Bundle class
+ * @param Cipher class
+ * @author Paul Schaub
+ */
+public class OmemoMessageBuilder {
+ private final OmemoStore omemoStore;
+ private final OmemoManager omemoManager;
+
+ private byte[] messageKey = generateKey();
+ private byte[] initializationVector = generateIv();
+
+ private byte[] ciphertextMessage;
+ private final ArrayList keys = new ArrayList<>();
+
+ /**
+ * Create a OmemoMessageBuilder.
+ *
+ * @param omemoManager OmemoManager of our device.
+ * @param omemoStore OmemoStore.
+ * @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
+ * @throws NoSuchAlgorithmException
+ * @throws IllegalBlockSizeException
+ * @throws UnsupportedEncodingException
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public OmemoMessageBuilder(OmemoManager omemoManager,
+ OmemoStore omemoStore,
+ byte[] aesKey, byte[] iv)
+ throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
+ UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
+ this.omemoStore = omemoStore;
+ this.omemoManager = omemoManager;
+ this.messageKey = aesKey;
+ this.initializationVector = iv;
+ }
+
+ /**
+ * Create a new OmemoMessageBuilder with random IV and AES key.
+ *
+ * @param omemoManager omemoManager of our device.
+ * @param omemoStore omemoStore.
+ * @param message Messages body.
+ * @throws NoSuchPaddingException
+ * @throws BadPaddingException
+ * @throws InvalidKeyException
+ * @throws NoSuchAlgorithmException
+ * @throws IllegalBlockSizeException
+ * @throws UnsupportedEncodingException
+ * @throws NoSuchProviderException
+ * @throws InvalidAlgorithmParameterException
+ */
+ public OmemoMessageBuilder(OmemoManager omemoManager,
+ OmemoStore omemoStore, String message)
+ throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
+ UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
+ this.omemoManager = omemoManager;
+ this.omemoStore = omemoStore;
+ this.setMessage(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.
+ *
+ * @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.
+ */
+ public void setMessage(String message) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
+ if (message == null) {
+ return;
+ }
+
+ //Encrypt message body
+ SecretKey secretKey = new SecretKeySpec(messageKey, KEYTYPE);
+ IvParameterSpec ivSpec = new IvParameterSpec(initializationVector);
+ Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
+
+ byte[] body;
+ byte[] ciphertext;
+
+ body = (message.getBytes(StringUtils.UTF8));
+ ciphertext = cipher.doFinal(body);
+
+ 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);
+
+ ciphertextMessage = cipherTextWithoutAuthTag;
+ messageKey = clearKeyWithAuthTag;
+ }
+
+ /**
+ * Add a new recipient device to the message.
+ *
+ * @param device recipient device
+ * @throws CryptoFailedException when encrypting the messageKey fails
+ * @throws UndecidedOmemoIdentityException
+ * @throws CorruptedOmemoKeyException
+ */
+ public void addRecipient(OmemoDevice device) throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException {
+ addRecipient(device, false);
+ }
+
+ /**
+ * Add a new recipient device to the message.
+ * @param device recipient device
+ * @param ignoreTrust ignore current trust state? Useful for keyTransportMessages that are sent to repair a session
+ * @throws CryptoFailedException
+ * @throws UndecidedOmemoIdentityException
+ * @throws CorruptedOmemoKeyException
+ */
+ public void addRecipient(OmemoDevice device, boolean ignoreTrust) throws
+ CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException {
+ OmemoSession session =
+ omemoStore.getOmemoSessionOf(omemoManager, device);
+
+ if (session != null) {
+ if (!ignoreTrust && !omemoStore.isDecidedOmemoIdentity(omemoManager, device, session.getIdentityKey())) {
+ //Warn user of undecided device
+ throw new UndecidedOmemoIdentityException(device);
+ }
+
+ if (!ignoreTrust && omemoStore.isTrustedOmemoIdentity(omemoManager, device, session.getIdentityKey())) {
+ //Encrypt key and save to header
+ CiphertextTuple encryptedKey = session.encryptMessageKey(messageKey);
+ keys.add(new OmemoVAxolotlElement.OmemoHeader.Key(encryptedKey.getCiphertext(), device.getDeviceId(), encryptedKey.isPreKeyMessage()));
+ }
+ }
+ }
+
+ /**
+ * Assemble an OmemoMessageElement from the current state of the builder.
+ *
+ * @return OmemoMessageElement
+ */
+ public OmemoVAxolotlElement finish() {
+ OmemoVAxolotlElement.OmemoHeader header = new OmemoVAxolotlElement.OmemoHeader(
+ omemoManager.getDeviceId(),
+ keys,
+ initializationVector
+ );
+ return new OmemoVAxolotlElement(header, ciphertextMessage);
+ }
+
+ /**
+ * Generate a new AES key used to encrypt the message.
+ *
+ * @return new AES key
+ * @throws NoSuchAlgorithmException
+ */
+ public static byte[] generateKey() throws NoSuchAlgorithmException {
+ KeyGenerator generator = KeyGenerator.getInstance(KEYTYPE);
+ generator.init(KEYLENGTH);
+ return generator.generateKey().getEncoded();
+ }
+
+ /**
+ * Generate a 16 byte initialization vector for AES encryption.
+ *
+ * @return iv
+ */
+ public static byte[] generateIv() {
+ SecureRandom random = new SecureRandom();
+ byte[] iv = new byte[16];
+ random.nextBytes(iv);
+ return iv;
+ }
+
+ public byte[] getCiphertextMessage() {
+ return ciphertextMessage;
+ }
+
+ public byte[] getMessageKey() {
+ return messageKey;
+ }
+}
diff --git a/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/package-info.java b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/package-info.java
new file mode 100644
index 000000000..af65de1e1
--- /dev/null
+++ b/smack-omemo/src/main/java/org/jivesoftware/smackx/omemo/util/package-info.java
@@ -0,0 +1,23 @@
+/**
+ *
+ * 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.
+ */
+/**
+ * Helper classes and other stuff.
+ *
+ * @author Paul Schaub
+ * @see XEP-XXXX: OMEMO
+ */
+package org.jivesoftware.smackx.omemo.util;
diff --git a/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.providers b/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.providers
new file mode 100644
index 000000000..463807d65
--- /dev/null
+++ b/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.providers
@@ -0,0 +1,19 @@
+
+
+
+
+ encrypted
+ eu.siacs.conversations.axolotl
+ org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider
+
+
+ list
+ eu.siacs.conversations.axolotl
+ org.jivesoftware.smackx.omemo.provider.OmemoDeviceListVAxolotlProvider
+
+
+ bundle
+ eu.siacs.conversations.axolotl
+ org.jivesoftware.smackx.omemo.provider.OmemoBundleVAxolotlProvider
+
+
diff --git a/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.xml b/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.xml
new file mode 100644
index 000000000..eb24fca42
--- /dev/null
+++ b/smack-omemo/src/main/resources/org.jivesoftware.smackx.omemo/omemo.xml
@@ -0,0 +1,5 @@
+
+
+ org.jivesoftware.smackx.omemo.OmemoManager
+
+
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/DeviceListTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/DeviceListTest.java
new file mode 100644
index 000000000..daf1383c7
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/DeviceListTest.java
@@ -0,0 +1,70 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
+import org.junit.Test;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+
+/**
+ * Test behavior of device lists.
+ *
+ * @author Paul Schaub
+ */
+public class DeviceListTest {
+
+
+ /**
+ * Test, whether deviceList updates are correctly merged into the cached device list.
+ * IDs in the update become active devices, active IDs that were not in the update become inactive.
+ * Inactive IDs that were not in the update stay inactive.
+ */
+ @Test
+ public void mergeDeviceListsTest() {
+ CachedDeviceList cached = new CachedDeviceList();
+ assertNotNull(cached.getActiveDevices());
+ assertNotNull(cached.getInactiveDevices());
+
+ cached.getInactiveDevices().add(1);
+ cached.getInactiveDevices().add(2);
+ cached.getActiveDevices().add(3);
+
+ Set update = new HashSet<>();
+ update.add(4);
+ update.add(1);
+
+ cached.merge(update);
+
+ assertTrue(cached.getActiveDevices().contains(1) &&
+ !cached.getActiveDevices().contains(2) &&
+ !cached.getActiveDevices().contains(3) &&
+ cached.getActiveDevices().contains(4));
+
+ assertTrue(!cached.getInactiveDevices().contains(1) &&
+ cached.getInactiveDevices().contains(2) &&
+ cached.getInactiveDevices().contains(3) &&
+ !cached.getInactiveDevices().contains(4));
+
+ assertTrue(cached.getAllDevices().size() == 4);
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoBundleVAxolotlElementTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoBundleVAxolotlElementTest.java
new file mode 100644
index 000000000..ea3b74b57
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoBundleVAxolotlElementTest.java
@@ -0,0 +1,96 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smack.test.util.SmackTestSuite;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.stringencoder.Base64;
+import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
+import org.jivesoftware.smackx.omemo.provider.OmemoBundleVAxolotlProvider;
+import org.junit.Test;
+
+import java.util.Arrays;
+import java.util.HashMap;
+
+import static junit.framework.TestCase.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test serialization and parsing of the OmemoBundleVAxolotlElement.
+ */
+public class OmemoBundleVAxolotlElementTest extends SmackTestSuite {
+
+ @Test
+ public void serializationTest() throws Exception {
+ int signedPreKeyId = 420;
+ String signedPreKeyB64 = Base64.encodeToString("SignedPreKey".getBytes(StringUtils.UTF8));
+ String signedPreKeySigB64 = Base64.encodeToString("SignedPreKeySignature".getBytes(StringUtils.UTF8));
+ String identityKeyB64 = Base64.encodeToString("IdentityKey".getBytes(StringUtils.UTF8));
+ int preKeyId1 = 220, preKeyId2 = 284;
+ String preKey1B64 = Base64.encodeToString("FirstPreKey".getBytes(StringUtils.UTF8)),
+ preKey2B64 = Base64.encodeToString("SecondPreKey".getBytes(StringUtils.UTF8));
+ HashMap preKeysB64 = new HashMap<>();
+ preKeysB64.put(preKeyId1, preKey1B64);
+ preKeysB64.put(preKeyId2, preKey2B64);
+
+ OmemoBundleVAxolotlElement bundle = new OmemoBundleVAxolotlElement(signedPreKeyId,
+ signedPreKeyB64, signedPreKeySigB64, identityKeyB64, preKeysB64);
+
+ assertEquals("ElementName must match.", "bundle", bundle.getElementName());
+ assertEquals("Namespace must match.", "eu.siacs.conversations.axolotl", bundle.getNamespace());
+
+ String expected =
+ "" +
+ "" +
+ signedPreKeyB64 +
+ "" +
+ "" +
+ signedPreKeySigB64 +
+ "" +
+ "" +
+ identityKeyB64 +
+ "" +
+ "" +
+ "" +
+ preKey1B64 +
+ "" +
+ "" +
+ preKey2B64 +
+ "" +
+ "" +
+ "";
+ String actual = bundle.toXML().toString();
+ assertEquals("Bundles XML must match.", expected, actual);
+
+ byte[] signedPreKey = "SignedPreKey".getBytes(StringUtils.UTF8);
+ byte[] signedPreKeySig = "SignedPreKeySignature".getBytes(StringUtils.UTF8);
+ byte[] identityKey = "IdentityKey".getBytes(StringUtils.UTF8);
+ byte[] firstPreKey = "FirstPreKey".getBytes(StringUtils.UTF8);
+ byte[] secondPreKey = "SecondPreKey".getBytes(StringUtils.UTF8);
+
+ OmemoBundleVAxolotlElement parsed = new OmemoBundleVAxolotlProvider().parse(TestUtils.getParser(actual));
+
+ assertTrue("B64-decoded signedPreKey must match.", Arrays.equals(signedPreKey, parsed.getSignedPreKey()));
+ assertEquals("SignedPreKeyId must match", signedPreKeyId, parsed.getSignedPreKeyId());
+ assertTrue("B64-decoded signedPreKey signature must match.", Arrays.equals(signedPreKeySig, parsed.getSignedPreKeySignature()));
+ assertTrue("B64-decoded identityKey must match.", Arrays.equals(identityKey, parsed.getIdentityKey()));
+ assertTrue("B64-decoded first preKey must match.", Arrays.equals(firstPreKey, parsed.getPreKey(220)));
+ assertTrue("B64-decoded second preKey must match.", Arrays.equals(secondPreKey, parsed.getPreKey(284)));
+ assertEquals("toString outputs must match.", bundle.toString(), parsed.toString());
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoConfigurationTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoConfigurationTest.java
new file mode 100644
index 000000000..171750519
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoConfigurationTest.java
@@ -0,0 +1,112 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import junit.framework.TestCase;
+import org.jivesoftware.smackx.omemo.OmemoConfiguration;
+import org.junit.Test;
+
+import java.io.File;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+
+/**
+ * Test the OmemoConfiguration class.
+ */
+public class OmemoConfigurationTest {
+
+ @Test
+ public void omemoConfigurationTest() {
+ @SuppressWarnings("unused") OmemoConfiguration configuration = new OmemoConfiguration();
+ // Default Store Path
+ File storePath = new File("test");
+ assertNull("getFileBasedOmemoStoreDefaultPath MUST return null at this point.",
+ OmemoConfiguration.getFileBasedOmemoStoreDefaultPath());
+ OmemoConfiguration.setFileBasedOmemoStoreDefaultPath(storePath);
+ assertEquals("FileBasedOmemoStoreDefaultPath must equal the one we set.", storePath.getAbsolutePath(),
+ OmemoConfiguration.getFileBasedOmemoStoreDefaultPath().getAbsolutePath());
+
+ // EME
+ OmemoConfiguration.setAddEmeEncryptionHint(false);
+ assertEquals(false, OmemoConfiguration.getAddEmeEncryptionHint());
+ OmemoConfiguration.setAddEmeEncryptionHint(true);
+ assertEquals(true, OmemoConfiguration.getAddEmeEncryptionHint());
+
+ // MAM
+ OmemoConfiguration.setAddMAMStorageProcessingHint(false);
+ assertEquals(false, OmemoConfiguration.getAddMAMStorageProcessingHint());
+ OmemoConfiguration.setAddMAMStorageProcessingHint(true);
+ assertEquals(true, OmemoConfiguration.getAddMAMStorageProcessingHint());
+
+ // Body hint
+ OmemoConfiguration.setAddOmemoHintBody(false);
+ assertEquals(false, OmemoConfiguration.getAddOmemoHintBody());
+ OmemoConfiguration.setAddOmemoHintBody(true);
+ assertEquals(true, OmemoConfiguration.getAddOmemoHintBody());
+
+ // Delete stale devices
+ OmemoConfiguration.setDeleteStaleDevices(false);
+ assertEquals(false, OmemoConfiguration.getDeleteStaleDevices());
+ OmemoConfiguration.setDeleteStaleDevices(true);
+ assertEquals(true, OmemoConfiguration.getDeleteStaleDevices());
+ OmemoConfiguration.setDeleteStaleDevicesAfterHours(25);
+ assertEquals(25, OmemoConfiguration.getDeleteStaleDevicesAfterHours());
+ try {
+ OmemoConfiguration.setDeleteStaleDevicesAfterHours(-3);
+ TestCase.fail("OmemoConfiguration.setDeleteStaleDevicesAfterHours should not accept values <= 0.");
+ } catch (IllegalArgumentException e) {
+ // Expected.
+ }
+
+ // Ignore stale device
+ OmemoConfiguration.setIgnoreStaleDevices(false);
+ assertEquals(false, OmemoConfiguration.getIgnoreStaleDevices());
+ OmemoConfiguration.setIgnoreStaleDevices(true);
+ assertEquals(true, OmemoConfiguration.getIgnoreStaleDevices());
+ OmemoConfiguration.setIgnoreStaleDevicesAfterHours(44);
+ assertEquals(44, OmemoConfiguration.getIgnoreStaleDevicesAfterHours());
+ try {
+ OmemoConfiguration.setIgnoreStaleDevicesAfterHours(-5);
+ TestCase.fail("OmemoConfiguration.setIgnoreStaleDevicesAfterHours should not accept values <= 0.");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+
+ // Renew signedPreKeys
+ OmemoConfiguration.setRenewOldSignedPreKeys(false);
+ assertEquals(false, OmemoConfiguration.getRenewOldSignedPreKeys());
+ OmemoConfiguration.setRenewOldSignedPreKeys(true);
+ assertEquals(true, OmemoConfiguration.getRenewOldSignedPreKeys());
+ OmemoConfiguration.setRenewOldSignedPreKeysAfterHours(77);
+ assertEquals(77, OmemoConfiguration.getRenewOldSignedPreKeysAfterHours());
+ try {
+ OmemoConfiguration.setRenewOldSignedPreKeysAfterHours(0);
+ TestCase.fail("OmemoConfiguration.setRenewOldSignedPreKeysAfterHours should not accept values <= 0");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ OmemoConfiguration.setMaxNumberOfStoredSignedPreKeys(6);
+ assertEquals(6, OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys());
+ try {
+ OmemoConfiguration.setMaxNumberOfStoredSignedPreKeys(0);
+ TestCase.fail("OmemoConfiguration.setMaxNumberOfStoredSignedPreKeys should not accept values <= 0");
+ } catch (IllegalArgumentException e) {
+ //Expected
+ }
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceListVAxolotlElementTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceListVAxolotlElementTest.java
new file mode 100644
index 000000000..dc32f2e34
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceListVAxolotlElementTest.java
@@ -0,0 +1,56 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smack.test.util.SmackTestSuite;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smackx.omemo.element.OmemoDeviceListVAxolotlElement;
+import org.jivesoftware.smackx.omemo.provider.OmemoDeviceListVAxolotlProvider;
+import org.junit.Test;
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.HashSet;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test serialization and parsing of DeviceListElement.
+ */
+public class OmemoDeviceListVAxolotlElementTest extends SmackTestSuite {
+
+ @Test
+ public void serializationTest() throws Exception {
+ HashSet ids = new HashSet<>();
+ ids.add(1234);
+ ids.add(9876);
+
+ OmemoDeviceListVAxolotlElement element = new OmemoDeviceListVAxolotlElement(ids);
+ String xml = element.toXML().toString();
+
+ XmlPullParser parser = TestUtils.getParser(xml);
+ OmemoDeviceListVAxolotlElement parsed = new OmemoDeviceListVAxolotlProvider().parse(parser);
+
+ assertTrue("Parsed element must equal the original.", parsed.getDeviceIds().equals(element.getDeviceIds()));
+ assertEquals("Generated XML must match.",
+ "" +
+ "" +
+ "" +
+ "
",
+ xml);
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceTest.java
new file mode 100644
index 000000000..91d666434
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoDeviceTest.java
@@ -0,0 +1,62 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
+import org.junit.Assert;
+import org.junit.Test;
+import org.jxmpp.jid.BareJid;
+import org.jxmpp.jid.impl.JidCreate;
+import org.jxmpp.stringprep.XmppStringprepException;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test the OmemoDevice class.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoDeviceTest {
+
+ /**
+ * Test, if the equals() method works as intended.
+ */
+ @Test
+ public void testEquals() {
+ BareJid romeo, juliet, guyUnderTheBalcony;
+ try {
+ romeo = JidCreate.bareFrom("romeo@shakespeare.lit");
+ guyUnderTheBalcony = JidCreate.bareFrom("romeo@shakespeare.lit/underTheBalcony");
+ juliet = JidCreate.bareFrom("juliet@shakespeare.lit");
+ } catch (XmppStringprepException e) {
+ Assert.fail(e.getMessage());
+ return;
+ }
+
+ OmemoDevice r = new OmemoDevice(romeo, 1);
+ OmemoDevice g = new OmemoDevice(guyUnderTheBalcony, 1);
+ OmemoDevice r2 = new OmemoDevice(romeo, 2);
+ OmemoDevice j = new OmemoDevice(juliet, 3);
+ OmemoDevice j2 = new OmemoDevice(juliet, 1);
+
+ assertTrue(r.equals(g));
+ assertFalse(r.equals(r2));
+ assertFalse(j.equals(j2));
+ assertFalse(j2.equals(r2));
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoExceptionsTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoExceptionsTest.java
new file mode 100644
index 000000000..2c3dfd4bc
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoExceptionsTest.java
@@ -0,0 +1,104 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
+import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
+import org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException;
+import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
+import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
+import org.junit.Test;
+import org.jxmpp.jid.impl.JidCreate;
+import org.jxmpp.stringprep.XmppStringprepException;
+
+import java.util.ArrayList;
+
+import static junit.framework.TestCase.fail;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Test Omemo related Exceptions.
+ */
+public class OmemoExceptionsTest {
+
+ @Test
+ public void undecidedOmemoIdentityExceptionTest() throws XmppStringprepException {
+ OmemoDevice alice = new OmemoDevice(JidCreate.bareFrom("alice@server.tld"), 1234);
+ OmemoDevice bob = new OmemoDevice(JidCreate.bareFrom("bob@server.tld"), 5678);
+ OmemoDevice mallory = new OmemoDevice(JidCreate.bareFrom("mallory@server.tld"), 9876);
+
+ UndecidedOmemoIdentityException u = new UndecidedOmemoIdentityException(alice);
+ assertTrue(u.getUntrustedDevices().contains(alice));
+ assertTrue(u.getUntrustedDevices().size() == 1);
+
+ UndecidedOmemoIdentityException v = new UndecidedOmemoIdentityException(bob);
+ v.getUntrustedDevices().add(mallory);
+ assertTrue(v.getUntrustedDevices().size() == 2);
+ assertTrue(v.getUntrustedDevices().contains(bob));
+ assertTrue(v.getUntrustedDevices().contains(mallory));
+
+ u.getUntrustedDevices().add(bob);
+ u.join(v);
+ assertTrue(u.getUntrustedDevices().size() == 3);
+ }
+
+ @Test
+ public void cannotEstablishOmemoSessionExceptionTest() throws XmppStringprepException {
+ OmemoDevice alice1 = new OmemoDevice(JidCreate.bareFrom("alice@server.tld"), 1234);
+ OmemoDevice alice2 = new OmemoDevice(JidCreate.bareFrom("alice@server.tld"), 2345);
+ OmemoDevice bob = new OmemoDevice(JidCreate.bareFrom("bob@server.tld"), 5678);
+
+ CannotEstablishOmemoSessionException c = new CannotEstablishOmemoSessionException(alice1, null);
+ assertEquals(1, c.getFailures().size());
+ assertTrue(c.getFailures().containsKey(alice1.getJid()));
+
+ c.addSuccess(alice2);
+ assertFalse(c.requiresThrowing());
+
+ c.addFailures(new CannotEstablishOmemoSessionException(bob, null));
+ assertTrue(c.requiresThrowing());
+ assertEquals(1, c.getSuccesses().size());
+ assertEquals(2, c.getFailures().size());
+
+ c.getSuccesses().remove(alice2.getJid());
+ c.addFailures(new CannotEstablishOmemoSessionException(alice2, null));
+ assertEquals(2, c.getFailures().size());
+ }
+
+ @Test
+ public void multipleCryptoFailedExceptionTest() {
+ CryptoFailedException e1 = new CryptoFailedException("Fail");
+ CryptoFailedException e2 = new CryptoFailedException("EpicFail");
+ ArrayList l = new ArrayList<>();
+ l.add(e1); l.add(e2);
+ MultipleCryptoFailedException m = MultipleCryptoFailedException.from(l);
+
+ assertEquals(2, m.getCryptoFailedExceptions().size());
+ assertTrue(m.getCryptoFailedExceptions().contains(e1));
+ assertTrue(m.getCryptoFailedExceptions().contains(e2));
+
+ ArrayList el = new ArrayList<>();
+ try {
+ MultipleCryptoFailedException m2 = MultipleCryptoFailedException.from(el);
+ fail("MultipleCryptoFailedException must not allow empty list.");
+ } catch (IllegalArgumentException e) {
+ // Expected
+ }
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoFingerprintTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoFingerprintTest.java
new file mode 100644
index 000000000..95d16d67b
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoFingerprintTest.java
@@ -0,0 +1,38 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smackx.omemo.OmemoFingerprint;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertNotSame;
+
+/**
+ * Test the OmemoFingerprint class.
+ */
+public class OmemoFingerprintTest {
+
+ @Test
+ public void fingerprintTest() {
+ OmemoFingerprint first = new OmemoFingerprint("FINGER");
+ OmemoFingerprint second = new OmemoFingerprint("TOE");
+
+ assertNotSame(first, second);
+ assertEquals(first, new OmemoFingerprint("FINGER"));
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoKeyUtilTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoKeyUtilTest.java
new file mode 100644
index 000000000..3bdb1835b
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoKeyUtilTest.java
@@ -0,0 +1,46 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
+import org.junit.Test;
+
+import static junit.framework.TestCase.assertEquals;
+
+/**
+ * Test KeyUtil functions.
+ *
+ * @author Paul Schaub
+ */
+public class OmemoKeyUtilTest {
+
+ @Test
+ public void testAddInBounds() {
+ int high = Integer.MAX_VALUE - 2;
+ int max = Integer.MAX_VALUE;
+ assertEquals(OmemoKeyUtil.addInBounds(high, 3), 1);
+ assertEquals(OmemoKeyUtil.addInBounds(1,2), 3);
+ assertEquals(OmemoKeyUtil.addInBounds(max, 5), 5);
+ }
+
+ @Test
+ public void testPrettyFingerprint() {
+ String ugly = "FFFFFFFFEEEEEEEEDDDDDDDDCCCCCCCCBBBBBBBBAAAAAAAA9999999988888888";
+ String pretty = OmemoKeyUtil.prettyFingerprint(ugly);
+ assertEquals(pretty, "FFFFFFFF EEEEEEEE DDDDDDDD CCCCCCCC BBBBBBBB AAAAAAAA 99999999 88888888");
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoVAxolotlElementTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoVAxolotlElementTest.java
new file mode 100644
index 000000000..e5a0f33dd
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/OmemoVAxolotlElementTest.java
@@ -0,0 +1,73 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.jivesoftware.smack.test.util.SmackTestSuite;
+import org.jivesoftware.smack.test.util.TestUtils;
+import org.jivesoftware.smack.util.StringUtils;
+import org.jivesoftware.smack.util.stringencoder.Base64;
+import org.jivesoftware.smackx.omemo.element.OmemoElement;
+import org.jivesoftware.smackx.omemo.element.OmemoVAxolotlElement;
+import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
+import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
+import org.junit.Test;
+
+import java.util.ArrayList;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * Test serialization and parsing of OmemoVAxolotlElements.
+ */
+public class OmemoVAxolotlElementTest extends SmackTestSuite {
+
+ @Test
+ public void serializationTest() throws Exception {
+ byte[] payload = "This is payload.".getBytes(StringUtils.UTF8);
+ int keyId1 = 8;
+ int keyId2 = 33333;
+ byte[] keyData1 = "KEYDATA".getBytes(StringUtils.UTF8);
+ byte[] keyData2 = "DATAKEY".getBytes(StringUtils.UTF8);
+ int sid = 12131415;
+ byte[] iv = OmemoMessageBuilder.generateIv();
+
+ ArrayList keys = new ArrayList<>();
+ keys.add(new OmemoElement.OmemoHeader.Key(keyData1, keyId1));
+ keys.add(new OmemoElement.OmemoHeader.Key(keyData2, keyId2, true));
+
+ OmemoVAxolotlElement.OmemoHeader header = new OmemoElement.OmemoHeader(sid, keys, iv);
+ OmemoVAxolotlElement element = new OmemoVAxolotlElement(header, payload);
+
+ String expected =
+ "" +
+ "" +
+ "" + Base64.encodeToString(keyData1) + "" +
+ "" + Base64.encodeToString(keyData2) + "" +
+ "" + Base64.encodeToString(iv) + "" +
+ "" +
+ "" +
+ Base64.encodeToString(payload) +
+ "" +
+ "";
+
+ String actual = element.toXML().toString();
+ assertEquals("Serialized xml of OmemoElement must match.", expected, actual);
+
+ OmemoVAxolotlElement parsed = new OmemoVAxolotlProvider().parse(TestUtils.getParser(actual));
+ assertEquals("Parsed OmemoElement must equal the original.", element.toXML().toString(), parsed.toXML().toString());
+ }
+}
diff --git a/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/WrapperObjectsTest.java b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/WrapperObjectsTest.java
new file mode 100644
index 000000000..1688c65fe
--- /dev/null
+++ b/smack-omemo/src/test/java/org/jivesoftware/smack/omemo/WrapperObjectsTest.java
@@ -0,0 +1,105 @@
+/**
+ *
+ * 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.smack.omemo;
+
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.jivesoftware.smack.packet.Message;
+import org.jivesoftware.smackx.omemo.element.OmemoElement;
+import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
+import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
+import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
+import org.jivesoftware.smackx.omemo.internal.ClearTextMessage;
+import org.jivesoftware.smackx.omemo.internal.IdentityKeyWrapper;
+import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
+import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
+import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
+import org.junit.Test;
+import org.jxmpp.jid.BareJid;
+import org.jxmpp.jid.impl.JidCreate;
+
+import java.security.NoSuchAlgorithmException;
+import java.security.Security;
+
+import static junit.framework.TestCase.assertEquals;
+import static junit.framework.TestCase.assertTrue;
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotNull;
+
+/**
+ * Test the identityKeyWrapper.
+ */
+public class WrapperObjectsTest {
+
+ @Test
+ public void identityKeyWrapperTest() {
+ Object pseudoKey = new Object();
+ IdentityKeyWrapper wrapper = new IdentityKeyWrapper(pseudoKey);
+ assertEquals(pseudoKey, wrapper.getIdentityKey());
+ }
+
+ @Test
+ public void ciphertextTupleTest() {
+ byte[] c = OmemoMessageBuilder.generateIv();
+ CiphertextTuple c1 = new CiphertextTuple(c, OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE);
+ assertTrue(c1.isPreKeyMessage());
+ assertArrayEquals(c, c1.getCiphertext());
+ assertEquals(OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE, c1.getMessageType());
+
+ CiphertextTuple c2 = new CiphertextTuple(c, OmemoElement.TYPE_OMEMO_MESSAGE);
+ assertFalse(c2.isPreKeyMessage());
+ assertEquals(OmemoElement.TYPE_OMEMO_MESSAGE, c2.getMessageType());
+ }
+
+ @Test
+ public void clearTextMessageTest() throws Exception {
+ Object pseudoKey = new Object();
+ IdentityKeyWrapper wrapper = new IdentityKeyWrapper(pseudoKey);
+ BareJid senderJid = JidCreate.bareFrom("bob@server.tld");
+ OmemoDevice sender = new OmemoDevice(senderJid, 1234);
+ OmemoMessageInformation information = new OmemoMessageInformation(wrapper, sender, OmemoMessageInformation.CARBON.NONE);
+
+ assertTrue("OmemoInformation must state that the message is an OMEMO message.",
+ information.isOmemoMessage());
+ assertEquals(OmemoMessageInformation.CARBON.NONE, information.getCarbon());
+ assertEquals(sender, information.getSenderDevice());
+ assertEquals(wrapper, information.getSenderIdentityKey());
+
+ String body = "Decrypted Body";
+ Message message = new Message(senderJid, body);
+ ClearTextMessage c = new ClearTextMessage(body, message, information);
+
+ assertEquals(message, c.getOriginalMessage());
+ assertEquals(information, c.getMessageInformation());
+ assertEquals(body, c.getBody());
+ }
+
+ @Test
+ public void cipherAndAuthTagTest() throws NoSuchAlgorithmException, CryptoFailedException {
+ Security.addProvider(new BouncyCastleProvider());
+ byte[] key = OmemoMessageBuilder.generateKey();
+ byte[] iv = OmemoMessageBuilder.generateIv();
+ byte[] authTag = OmemoMessageBuilder.generateIv();
+
+ CipherAndAuthTag cat = new CipherAndAuthTag(key, iv, authTag);
+
+ assertNotNull(cat.getCipher());
+ assertArrayEquals(key, cat.getKey());
+ assertArrayEquals(iv, cat.getIv());
+ assertArrayEquals(authTag, cat.getAuthTag());
+ }
+}
diff --git a/smack-repl/build.gradle b/smack-repl/build.gradle
index c5b05db56..3f412fb50 100644
--- a/smack-repl/build.gradle
+++ b/smack-repl/build.gradle
@@ -16,6 +16,8 @@ dependencies {
testCompile project(path: ":smack-core", configuration: "archives")
}
-task printClasspath(dependsOn: assemble) << {
- println sourceSets.main.runtimeClasspath.asPath
+task printClasspath(dependsOn: assemble) {
+ doLast {
+ println sourceSets.main.runtimeClasspath.asPath
+ }
}