mirror of
https://github.com/vanitasvitae/Smack.git
synced 2024-11-27 14:32:06 +01:00
Reimplement encryption, solve compiler errors
This commit is contained in:
parent
e5cdd89edc
commit
50f8a4f1fe
20 changed files with 497 additions and 234 deletions
|
@ -64,7 +64,7 @@ public abstract class AbstractTwoUsersOmemoIntegrationTest extends AbstractOmemo
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterClass
|
@AfterClass
|
||||||
public void cleanUp() throws SmackException.NotLoggedInException {
|
public void cleanUp() {
|
||||||
alice.stopListeners();
|
alice.stopListeners();
|
||||||
bob.stopListeners();
|
bob.stopListeners();
|
||||||
OmemoManagerSetupHelper.cleanUpPubSub(alice);
|
OmemoManagerSetupHelper.cleanUpPubSub(alice);
|
||||||
|
|
|
@ -22,9 +22,8 @@ import static org.junit.Assert.assertFalse;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
@ -51,13 +50,13 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
|
||||||
OmemoBundleElement bobsBundle1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
OmemoBundleElement bobsBundle1 = bob.getOmemoService().getOmemoStoreBackend().packOmemoBundle(bob.getOwnDevice());
|
||||||
|
|
||||||
final String message1 = "One is greater than zero (for small values of zero).";
|
final String message1 = "One is greater than zero (for small values of zero).";
|
||||||
Message encrypted1 = alice.encrypt(bob.getOwnJid(), message1);
|
OmemoMessage.Sent encrypted1 = alice.encrypt(bob.getOwnJid(), message1);
|
||||||
final SimpleResultSyncPoint bobReceivedMessage = new SimpleResultSyncPoint();
|
final SimpleResultSyncPoint bobReceivedMessage = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
bob.addOmemoMessageListener(new OmemoMessageListener() {
|
bob.addOmemoMessageListener(new OmemoMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
|
||||||
if (decryptedBody.equals(message1)) {
|
if (received.getMessage().equals(message1)) {
|
||||||
bobReceivedMessage.signal();
|
bobReceivedMessage.signal();
|
||||||
} else {
|
} else {
|
||||||
bobReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
|
bobReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
|
||||||
|
@ -65,13 +64,16 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
|
||||||
// Not used
|
// Not needed
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
encrypted1.setTo(bob.getOwnJid());
|
Message m1 = new Message();
|
||||||
alice.getConnection().sendStanza(encrypted1);
|
m1.addExtension(encrypted1.getElement());
|
||||||
|
m1.setTo(bob.getOwnJid());
|
||||||
|
alice.getConnection().sendStanza(m1);
|
||||||
bobReceivedMessage.waitForResult(10 * 1000);
|
bobReceivedMessage.waitForResult(10 * 1000);
|
||||||
|
|
||||||
OmemoBundleElement aliceBundle2 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
OmemoBundleElement aliceBundle2 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
||||||
|
@ -82,13 +84,13 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
|
||||||
assertFalse(bobsBundle1.equals(bobsBundle2));
|
assertFalse(bobsBundle1.equals(bobsBundle2));
|
||||||
|
|
||||||
final String message2 = "The german words for 'leek' and 'wimp' are the same.";
|
final String message2 = "The german words for 'leek' and 'wimp' are the same.";
|
||||||
final Message encrypted2 = bob.encrypt(alice.getOwnJid(), message2);
|
final OmemoMessage.Sent encrypted2 = bob.encrypt(alice.getOwnJid(), message2);
|
||||||
final SimpleResultSyncPoint aliceReceivedMessage = new SimpleResultSyncPoint();
|
final SimpleResultSyncPoint aliceReceivedMessage = new SimpleResultSyncPoint();
|
||||||
|
|
||||||
alice.addOmemoMessageListener(new OmemoMessageListener() {
|
alice.addOmemoMessageListener(new OmemoMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
|
||||||
if (decryptedBody.equals(message2)) {
|
if (received.getMessage().equals(message2)) {
|
||||||
aliceReceivedMessage.signal();
|
aliceReceivedMessage.signal();
|
||||||
} else {
|
} else {
|
||||||
aliceReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
|
aliceReceivedMessage.signalFailure("Received decrypted message was not equal to sent message.");
|
||||||
|
@ -96,13 +98,15 @@ public class MessageEncryptionIntegrationTest extends AbstractTwoUsersOmemoInteg
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
|
||||||
// Not needed here either.
|
// Not needed
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
encrypted2.setTo(alice.getOwnJid());
|
Message m2 = new Message();
|
||||||
bob.getConnection().sendStanza(encrypted2);
|
m2.addExtension(encrypted2.getElement());
|
||||||
|
m2.setTo(alice.getOwnJid());
|
||||||
|
bob.getConnection().sendStanza(m2);
|
||||||
aliceReceivedMessage.waitForResult(10 * 1000);
|
aliceReceivedMessage.waitForResult(10 * 1000);
|
||||||
|
|
||||||
OmemoBundleElement aliceBundle3 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
OmemoBundleElement aliceBundle3 = alice.getOmemoService().getOmemoStoreBackend().packOmemoBundle(alice.getOwnDevice());
|
||||||
|
|
|
@ -25,7 +25,6 @@ import java.util.HashMap;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smackx.omemo.util.EphemeralTrustCallback;
|
|
||||||
import org.jivesoftware.smack.packet.Presence;
|
import org.jivesoftware.smack.packet.Presence;
|
||||||
import org.jivesoftware.smack.roster.PresenceEventListener;
|
import org.jivesoftware.smack.roster.PresenceEventListener;
|
||||||
import org.jivesoftware.smack.roster.Roster;
|
import org.jivesoftware.smack.roster.Roster;
|
||||||
|
@ -35,6 +34,7 @@ import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
|
import org.jivesoftware.smackx.omemo.util.EphemeralTrustCallback;
|
||||||
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
import org.jivesoftware.smackx.omemo.util.OmemoConstants;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
|
@ -45,6 +45,7 @@ import org.jxmpp.jid.BareJid;
|
||||||
import org.jxmpp.jid.FullJid;
|
import org.jxmpp.jid.FullJid;
|
||||||
import org.jxmpp.jid.Jid;
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
|
|
||||||
public class OmemoManagerSetupHelper {
|
public class OmemoManagerSetupHelper {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -177,14 +178,11 @@ public class OmemoManagerSetupHelper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void cleanUpPubSub(OmemoManager omemoManager)
|
public static void cleanUpPubSub(OmemoManager omemoManager) {
|
||||||
throws SmackException.NotLoggedInException, XMPPException.XMPPErrorException,
|
|
||||||
PubSubException.NotALeafNodeException
|
|
||||||
{
|
|
||||||
PubSubManager pm = PubSubManager.getInstance(omemoManager.getConnection(),omemoManager.getOwnJid());
|
PubSubManager pm = PubSubManager.getInstance(omemoManager.getConnection(),omemoManager.getOwnJid());
|
||||||
try {
|
try {
|
||||||
omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid());
|
omemoManager.requestDeviceListUpdateFor(omemoManager.getOwnJid());
|
||||||
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException e) {
|
} catch (SmackException.NotConnectedException | InterruptedException | SmackException.NoResponseException | PubSubException.NotALeafNodeException | XMPPException.XMPPErrorException e) {
|
||||||
// ignore
|
// ignore
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -23,8 +23,7 @@ import java.util.concurrent.TimeoutException;
|
||||||
import org.jivesoftware.smack.SmackException;
|
import org.jivesoftware.smack.SmackException;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
||||||
|
|
||||||
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
import org.igniterealtime.smack.inttest.SmackIntegrationTest;
|
||||||
|
@ -41,16 +40,16 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
super(environment);
|
super(environment);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String m1 = "P = NP is true for all N,P from the set of complex numbers, where P is equal to 0";
|
private static final String body1 = "P = NP is true for all N,P from the set of complex numbers, where P is equal to 0";
|
||||||
private final SimpleResultSyncPoint bsp1 = new SimpleResultSyncPoint();
|
private final SimpleResultSyncPoint bsp1 = new SimpleResultSyncPoint();
|
||||||
private final OmemoMessageListener bml1 = new OmemoTestMessageListener(m1, bsp1);
|
private final OmemoMessageListener bml1 = new OmemoTestMessageListener(body1, bsp1);
|
||||||
|
|
||||||
private static final String m2 = "P = NP is also true for all N,P from the set of complex numbers, where N is equal to 1.";
|
private static final String body2 = "P = NP is also true for all N,P from the set of complex numbers, where N is equal to 1.";
|
||||||
private final ResultSyncPoint<Boolean, IllegalStateException> bsp2 = new ResultSyncPoint<>();
|
private final ResultSyncPoint<Boolean, IllegalStateException> bsp2 = new ResultSyncPoint<>();
|
||||||
private final OmemoMessageListener bml2 = new OmemoMessageListener() {
|
private final OmemoMessageListener bml2 = new OmemoMessageListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
|
||||||
if (decryptedBody.equals(m2)) {
|
if (received.getMessage().equals(body2)) {
|
||||||
bsp2.signal(new IllegalStateException("Message MUST NOT be decryptable!"));
|
bsp2.signal(new IllegalStateException("Message MUST NOT be decryptable!"));
|
||||||
} else {
|
} else {
|
||||||
bsp2.signal(new IllegalStateException("OmemoMessageListener MUST NOT be called for this message."));
|
bsp2.signal(new IllegalStateException("OmemoMessageListener MUST NOT be called for this message."));
|
||||||
|
@ -58,37 +57,40 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
|
||||||
|
// Not needed
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
private final SimpleResultSyncPoint asp2 = new SimpleResultSyncPoint();
|
private final SimpleResultSyncPoint asp2 = new SimpleResultSyncPoint();
|
||||||
private final OmemoMessageListener aml2 = new OmemoMessageListener() {
|
private final OmemoMessageListener aml2 = new OmemoMessageListener() {
|
||||||
@Override
|
|
||||||
public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage) {
|
||||||
|
// Not needed
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received received) {
|
||||||
asp2.signal();
|
asp2.signal();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final String m3 = "P = NP would be a disaster for the world of cryptography.";
|
private static final String body3 = "P = NP would be a disaster for the world of cryptography.";
|
||||||
private final SimpleResultSyncPoint bsp3 = new SimpleResultSyncPoint();
|
private final SimpleResultSyncPoint bsp3 = new SimpleResultSyncPoint();
|
||||||
private final OmemoMessageListener bml3 = new OmemoTestMessageListener(m3, bsp3);
|
private final OmemoMessageListener bml3 = new OmemoTestMessageListener(body3, bsp3);
|
||||||
|
|
||||||
@SmackIntegrationTest
|
@SmackIntegrationTest
|
||||||
public void sessionRenegotiationTest() throws Exception {
|
public void sessionRenegotiationTest() throws Exception {
|
||||||
/*
|
/*
|
||||||
Send (PreKey-)message from Alice to Bob to initiate a session.
|
Send (PreKey-)message from Alice to Bob to initiate a session.
|
||||||
*/
|
*/
|
||||||
Message e1 = alice.encrypt(bob.getOwnJid(), m1);
|
OmemoMessage.Sent e1 = alice.encrypt(bob.getOwnJid(), body1);
|
||||||
e1.setTo(bob.getOwnJid());
|
Message m1 = new Message();
|
||||||
|
m1.addExtension(e1.getElement());
|
||||||
|
m1.setTo(bob.getOwnJid());
|
||||||
|
|
||||||
bob.addOmemoMessageListener(bml1);
|
bob.addOmemoMessageListener(bml1);
|
||||||
alice.getConnection().sendStanza(e1);
|
alice.getConnection().sendStanza(m1);
|
||||||
bsp1.waitForResult(10 * 1000);
|
bsp1.waitForResult(10 * 1000);
|
||||||
bob.removeOmemoMessageListener(bml1);
|
bob.removeOmemoMessageListener(bml1);
|
||||||
|
|
||||||
|
@ -100,12 +102,14 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
/*
|
/*
|
||||||
Send normal message from Alice to Bob (Alice assumes, that Bob still has a valid session).
|
Send normal message from Alice to Bob (Alice assumes, that Bob still has a valid session).
|
||||||
*/
|
*/
|
||||||
Message e2 = alice.encrypt(bob.getOwnJid(), m2);
|
OmemoMessage.Sent e2 = alice.encrypt(bob.getOwnJid(), body2);
|
||||||
e2.setTo(bob.getOwnJid());
|
Message m2 = new Message();
|
||||||
|
m2.addExtension(e2.getElement());
|
||||||
|
m2.setTo(bob.getOwnJid());
|
||||||
|
|
||||||
bob.addOmemoMessageListener(bml2);
|
bob.addOmemoMessageListener(bml2);
|
||||||
alice.addOmemoMessageListener(aml2);
|
alice.addOmemoMessageListener(aml2);
|
||||||
alice.getConnection().sendStanza(e2);
|
alice.getConnection().sendStanza(m2);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Wait for the timeout on Bobs side, since message decryption will fail now.
|
Wait for the timeout on Bobs side, since message decryption will fail now.
|
||||||
|
@ -127,11 +131,13 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
Since Bob responded with a PreKeyMessage to repair the broken session, Alice should now be able to send messages
|
Since Bob responded with a PreKeyMessage to repair the broken session, Alice should now be able to send messages
|
||||||
which Bob can decrypt successfully again.
|
which Bob can decrypt successfully again.
|
||||||
*/
|
*/
|
||||||
Message e3 = alice.encrypt(bob.getOwnJid(), m3);
|
OmemoMessage.Sent e3 = alice.encrypt(bob.getOwnJid(), body3);
|
||||||
e3.setTo(bob.getOwnJid());
|
Message m3 = new Message();
|
||||||
|
m3.addExtension(e3.getElement());
|
||||||
|
m3.setTo(bob.getOwnJid());
|
||||||
|
|
||||||
bob.addOmemoMessageListener(bml3);
|
bob.addOmemoMessageListener(bml3);
|
||||||
alice.getConnection().sendStanza(e3);
|
alice.getConnection().sendStanza(m3);
|
||||||
bsp3.waitForResult(10 * 1000);
|
bsp3.waitForResult(10 * 1000);
|
||||||
bob.removeOmemoMessageListener(bml3);
|
bob.removeOmemoMessageListener(bml3);
|
||||||
}
|
}
|
||||||
|
@ -147,8 +153,8 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageReceived(String decryptedBody, Message encryptedMessage, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received received) {
|
||||||
if (decryptedBody.equals(expectedMessage)) {
|
if (received.getMessage().equals(expectedMessage)) {
|
||||||
syncPoint.signal();
|
syncPoint.signal();
|
||||||
} else {
|
} else {
|
||||||
syncPoint.signalFailure("Received decrypted message was not equal to sent message.");
|
syncPoint.signalFailure("Received decrypted message was not equal to sent message.");
|
||||||
|
@ -156,8 +162,8 @@ public class SessionRenegotiationIntegrationTest extends AbstractTwoUsersOmemoIn
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoKeyTransportReceived(CipherAndAuthTag cipherAndAuthTag, Message message, Message wrappingMessage, OmemoMessageInformation omemoInformation) {
|
public void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage) {
|
||||||
// Ignored.
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,19 +20,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.signal;
|
package org.jivesoftware.smackx.omemo.signal;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.XMPPException.XMPPErrorException;
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||||
import org.jivesoftware.smackx.omemo.OmemoService;
|
import org.jivesoftware.smackx.omemo.OmemoService;
|
||||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||||
|
@ -73,11 +62,7 @@ public final class SignalOmemoService
|
||||||
return new SignalOmemoRatchet(manager, getOmemoStoreBackend());
|
return new SignalOmemoRatchet(manager, getOmemoStoreBackend());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void setup()
|
public static void setup() {
|
||||||
throws InvalidKeyException, XMPPErrorException, NoSuchPaddingException, InvalidAlgorithmParameterException,
|
|
||||||
UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException,
|
|
||||||
NoSuchProviderException, SmackException, InterruptedException, CorruptedOmemoKeyException
|
|
||||||
{
|
|
||||||
if (!LICENSE_ACKNOWLEDGED) {
|
if (!LICENSE_ACKNOWLEDGED) {
|
||||||
throw new IllegalStateException("smack-omemo-signal is licensed under the terms of the GPLv3. " +
|
throw new IllegalStateException("smack-omemo-signal is licensed under the terms of the GPLv3. " +
|
||||||
"Please be aware that you can only use this library within the terms of the GPLv3. " +
|
"Please be aware that you can only use this library within the terms of the GPLv3. " +
|
||||||
|
@ -98,11 +83,7 @@ public final class SignalOmemoService
|
||||||
return new SignalCachingOmemoStore();
|
return new SignalCachingOmemoStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
private SignalOmemoService()
|
private SignalOmemoService() {
|
||||||
throws SmackException, InterruptedException, XMPPException.XMPPErrorException, CorruptedOmemoKeyException,
|
|
||||||
NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException,
|
|
||||||
IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException,
|
|
||||||
java.security.InvalidKeyException {
|
|
||||||
super();
|
super();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -225,18 +225,19 @@ public class SignalOmemoStoreConnector
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
|
public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
|
||||||
return omemoStore.loadOmemoSignedPreKey(getOurDevice(), i);
|
SignedPreKeyRecord signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i);
|
||||||
|
if (signedPreKeyRecord == null) {
|
||||||
|
throw new InvalidKeyIdException("No signed preKey with id " + i + " found.");
|
||||||
|
}
|
||||||
|
return signedPreKeyRecord;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
||||||
List<SignedPreKeyRecord> signedPreKeyRecordList = new ArrayList<>();
|
|
||||||
|
|
||||||
TreeMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap =
|
TreeMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap =
|
||||||
omemoStore.loadOmemoSignedPreKeys(getOurDevice());
|
omemoStore.loadOmemoSignedPreKeys(getOurDevice());
|
||||||
signedPreKeyRecordList.addAll(signedPreKeyRecordHashMap.values());
|
return new ArrayList<>(signedPreKeyRecordHashMap.values());
|
||||||
|
|
||||||
return signedPreKeyRecordList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -259,7 +260,7 @@ public class SignalOmemoStoreConnector
|
||||||
omemoStore.removeOmemoSignedPreKey(getOurDevice(), i);
|
omemoStore.removeOmemoSignedPreKey(getOurDevice(), i);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static OmemoDevice asOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
|
private static OmemoDevice asOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
|
||||||
return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
|
return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo;
|
package org.jivesoftware.smackx.omemo;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ public class SignalOmemoKeyUtilTest
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parameterized.Parameters
|
@Parameterized.Parameters
|
||||||
public static Collection<Object[]> getParameters() throws IOException {
|
public static Collection<Object[]> getParameters() {
|
||||||
return Arrays.asList(new Object[][] {
|
return Arrays.asList(new Object[][] {
|
||||||
{ new SignalOmemoKeyUtil()}
|
{ new SignalOmemoKeyUtil()}
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,23 +26,11 @@ import static junit.framework.TestCase.assertNotNull;
|
||||||
import static junit.framework.TestCase.assertNotSame;
|
import static junit.framework.TestCase.assertNotSame;
|
||||||
import static junit.framework.TestCase.assertTrue;
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.NoSuchProviderException;
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
|
|
||||||
import org.jivesoftware.smack.DummyConnection;
|
import org.jivesoftware.smack.DummyConnection;
|
||||||
import org.jivesoftware.smack.SmackException;
|
|
||||||
import org.jivesoftware.smack.XMPPException;
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||||
import org.jivesoftware.smack.test.util.TestUtils;
|
import org.jivesoftware.smack.test.util.TestUtils;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
|
||||||
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
import org.jivesoftware.smackx.omemo.provider.OmemoVAxolotlProvider;
|
||||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoService;
|
import org.jivesoftware.smackx.omemo.signal.SignalOmemoService;
|
||||||
|
|
||||||
|
@ -54,12 +42,7 @@ import org.junit.Test;
|
||||||
public class SignalOmemoManagerTest extends SmackTestSuite {
|
public class SignalOmemoManagerTest extends SmackTestSuite {
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void instantiationTest()
|
public void instantiationTest() {
|
||||||
throws CorruptedOmemoKeyException, NoSuchAlgorithmException, UnsupportedEncodingException,
|
|
||||||
InvalidKeyException, InterruptedException, XMPPException.XMPPErrorException, NoSuchPaddingException,
|
|
||||||
BadPaddingException, InvalidAlgorithmParameterException, NoSuchProviderException, IllegalBlockSizeException,
|
|
||||||
SmackException
|
|
||||||
{
|
|
||||||
SignalOmemoService.acknowledgeLicense();
|
SignalOmemoService.acknowledgeLicense();
|
||||||
SignalOmemoService.setup();
|
SignalOmemoService.setup();
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@ import java.util.Random;
|
||||||
import java.util.SortedSet;
|
import java.util.SortedSet;
|
||||||
import java.util.TreeMap;
|
import java.util.TreeMap;
|
||||||
import java.util.WeakHashMap;
|
import java.util.WeakHashMap;
|
||||||
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
import org.jivesoftware.smack.AbstractConnectionListener;
|
import org.jivesoftware.smack.AbstractConnectionListener;
|
||||||
|
@ -38,6 +39,7 @@ import org.jivesoftware.smack.StanzaListener;
|
||||||
import org.jivesoftware.smack.XMPPConnection;
|
import org.jivesoftware.smack.XMPPConnection;
|
||||||
import org.jivesoftware.smack.XMPPException;
|
import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.filter.StanzaFilter;
|
import org.jivesoftware.smack.filter.StanzaFilter;
|
||||||
|
import org.jivesoftware.smack.packet.ExtensionElement;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
@ -52,22 +54,26 @@ import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||||
import org.jivesoftware.smackx.muc.RoomInfo;
|
import org.jivesoftware.smackx.muc.RoomInfo;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||||
|
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||||
|
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException;
|
import org.jivesoftware.smackx.omemo.exceptions.NoOmemoSupportException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
import org.jivesoftware.smackx.omemo.listener.OmemoMessageListener;
|
||||||
import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener;
|
import org.jivesoftware.smackx.omemo.listener.OmemoMucMessageListener;
|
||||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
||||||
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
||||||
|
import org.jivesoftware.smackx.pep.PEPListener;
|
||||||
import org.jivesoftware.smackx.pep.PEPManager;
|
import org.jivesoftware.smackx.pep.PEPManager;
|
||||||
|
import org.jivesoftware.smackx.pubsub.EventElement;
|
||||||
|
import org.jivesoftware.smackx.pubsub.ItemsExtension;
|
||||||
|
import org.jivesoftware.smackx.pubsub.PayloadItem;
|
||||||
import org.jivesoftware.smackx.pubsub.PubSubException;
|
import org.jivesoftware.smackx.pubsub.PubSubException;
|
||||||
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
import org.jivesoftware.smackx.pubsub.packet.PubSub;
|
||||||
|
|
||||||
|
@ -245,7 +251,7 @@ public final class OmemoManager extends Manager {
|
||||||
*
|
*
|
||||||
* @param finishedCallback callback that gets called once the manager is initialized.
|
* @param finishedCallback callback that gets called once the manager is initialized.
|
||||||
*/
|
*/
|
||||||
public void initializeAsync(final FinishedCallback finishedCallback) {
|
public void initializeAsync(final InitializationFinishedCallback finishedCallback) {
|
||||||
Async.go(new Runnable() {
|
Async.go(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
@ -287,8 +293,8 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public OmemoMessage.Sent encrypt(BareJid recipient, String message)
|
public OmemoMessage.Sent encrypt(BareJid recipient, String message)
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
throws CryptoFailedException, UndecidedOmemoIdentityException,
|
||||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
InterruptedException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
|
@ -314,8 +320,8 @@ public final class OmemoManager extends Manager {
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
|
public OmemoMessage.Sent encrypt(ArrayList<BareJid> recipients, String message)
|
||||||
throws CryptoFailedException, UndecidedOmemoIdentityException, NoSuchAlgorithmException,
|
throws CryptoFailedException, UndecidedOmemoIdentityException,
|
||||||
InterruptedException, CannotEstablishOmemoSessionException, SmackException.NotConnectedException,
|
InterruptedException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException, SmackException.NotLoggedInException
|
SmackException.NoResponseException, SmackException.NotLoggedInException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
|
@ -346,9 +352,9 @@ public final class OmemoManager extends Manager {
|
||||||
* with any of their devices.
|
* with any of their devices.
|
||||||
*/
|
*/
|
||||||
public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
|
public OmemoMessage.Sent encrypt(MultiUserChat muc, String message)
|
||||||
throws UndecidedOmemoIdentityException, NoSuchAlgorithmException, CryptoFailedException,
|
throws UndecidedOmemoIdentityException, CryptoFailedException,
|
||||||
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
XMPPException.XMPPErrorException, SmackException.NotConnectedException, InterruptedException,
|
||||||
SmackException.NoResponseException, NoOmemoSupportException, CannotEstablishOmemoSessionException,
|
SmackException.NoResponseException, NoOmemoSupportException,
|
||||||
SmackException.NotLoggedInException
|
SmackException.NotLoggedInException
|
||||||
{
|
{
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
|
@ -722,6 +728,10 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the deviceId of the manager to nDeviceId.
|
||||||
|
* @param nDeviceId new deviceId
|
||||||
|
*/
|
||||||
void setDeviceId(int nDeviceId) {
|
void setDeviceId(int nDeviceId) {
|
||||||
synchronized (LOCK) {
|
synchronized (LOCK) {
|
||||||
// Move this instance inside the HashMaps
|
// Move this instance inside the HashMaps
|
||||||
|
@ -735,64 +745,58 @@ public final class OmemoManager extends Manager {
|
||||||
/**
|
/**
|
||||||
* Notify all registered OmemoMessageListeners about a received OmemoMessage.
|
* Notify all registered OmemoMessageListeners about a received OmemoMessage.
|
||||||
*
|
*
|
||||||
* @param decryptedBody decrypted Body element of the message
|
* @param stanza original stanza
|
||||||
* @param encryptedMessage unmodified message as it was received
|
* @param decryptedMessage decrypted OmemoMessage.
|
||||||
* @param wrappingMessage message that wrapped the incoming message
|
|
||||||
* @param messageInformation information about the messages encryption (used identityKey, carbon...)
|
|
||||||
*/
|
*/
|
||||||
void notifyOmemoMessageReceived(String decryptedBody,
|
void notifyOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage)
|
||||||
Message encryptedMessage,
|
|
||||||
Message wrappingMessage,
|
|
||||||
OmemoMessageInformation messageInformation)
|
|
||||||
{
|
{
|
||||||
for (OmemoMessageListener l : omemoMessageListeners) {
|
for (OmemoMessageListener l : omemoMessageListeners) {
|
||||||
l.onOmemoMessageReceived(decryptedBody, encryptedMessage, wrappingMessage, messageInformation);
|
l.onOmemoMessageReceived(stanza, decryptedMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void notifyOmemoKeyTransportMessageReceived(CipherAndAuthTag cipherAndAuthTag,
|
/**
|
||||||
Message transportingMessage,
|
* Notify all registered OmemoMessageListeners about a received OMEMO KeyTransportMessage.
|
||||||
Message wrappingMessage,
|
*
|
||||||
OmemoMessageInformation information)
|
* @param stanza original stanza.
|
||||||
|
* @param decryptedKeyTransportMessage decrypted KeyTransportMessage.
|
||||||
|
*/
|
||||||
|
void notifyOmemoKeyTransportMessageReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage)
|
||||||
{
|
{
|
||||||
for (OmemoMessageListener l : omemoMessageListeners) {
|
for (OmemoMessageListener l : omemoMessageListeners) {
|
||||||
l.onOmemoKeyTransportReceived(cipherAndAuthTag, transportingMessage, wrappingMessage, information);
|
l.onOmemoKeyTransportReceived(stanza, decryptedKeyTransportMessage);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Notify all registered OmemoMucMessageListeners of an incoming OmemoMessageElement in a MUC.
|
* Notify all registered OmemoMucMessageListeners of an incoming OmemoMessageElement in a MUC.
|
||||||
*
|
*
|
||||||
* @param muc MultiUserChat the message was received in
|
* @param muc MultiUserChat the message was received in.
|
||||||
* @param from BareJid of the user that sent the message
|
* @param stanza Original Stanza.
|
||||||
* @param decryptedBody decrypted body
|
* @param decryptedMessage Decryped OmemoMessage.
|
||||||
* @param message original message with encrypted content
|
|
||||||
* @param wrappingMessage wrapping message (in case of carbon copy)
|
|
||||||
* @param omemoInformation information about the encryption of the message
|
|
||||||
*/
|
*/
|
||||||
void notifyOmemoMucMessageReceived(MultiUserChat muc,
|
void notifyOmemoMucMessageReceived(MultiUserChat muc,
|
||||||
BareJid from,
|
Stanza stanza,
|
||||||
String decryptedBody,
|
OmemoMessage.Received decryptedMessage)
|
||||||
Message message,
|
|
||||||
Message wrappingMessage,
|
|
||||||
OmemoMessageInformation omemoInformation)
|
|
||||||
{
|
{
|
||||||
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
||||||
l.onOmemoMucMessageReceived(muc, from, decryptedBody, message,
|
l.onOmemoMucMessageReceived(muc, stanza, decryptedMessage);
|
||||||
wrappingMessage, omemoInformation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify registered OmemoMucMessageReceived listeners about KeyTransportMessages sent in a MUC.
|
||||||
|
*
|
||||||
|
* @param muc MultiUserChat in which the message was sent
|
||||||
|
* @param stanza original stanza
|
||||||
|
* @param decryptedKeyTransportMessage decrypted OMEMO KeyTransportElement
|
||||||
|
*/
|
||||||
void notifyOmemoMucKeyTransportMessageReceived(MultiUserChat muc,
|
void notifyOmemoMucKeyTransportMessageReceived(MultiUserChat muc,
|
||||||
BareJid from,
|
Stanza stanza,
|
||||||
CipherAndAuthTag cipherAndAuthTag,
|
OmemoMessage.Received decryptedKeyTransportMessage)
|
||||||
Message transportingMessage,
|
|
||||||
Message wrappingMessage,
|
|
||||||
OmemoMessageInformation messageInformation)
|
|
||||||
{
|
{
|
||||||
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
for (OmemoMucMessageListener l : omemoMucMessageListeners) {
|
||||||
l.onOmemoKeyTransportReceived(muc, from, cipherAndAuthTag,
|
l.onOmemoKeyTransportReceived(muc, stanza, decryptedKeyTransportMessage);
|
||||||
transportingMessage, wrappingMessage, messageInformation);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -808,10 +812,10 @@ public final class OmemoManager extends Manager {
|
||||||
// Remove listeners to avoid them getting added twice
|
// Remove listeners to avoid them getting added twice
|
||||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||||
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
carbonManager.removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
//pepManager.removePEPListener(deviceListUpdateListener);
|
pepManager.removePEPListener(deviceListUpdateListener);
|
||||||
|
|
||||||
// Add listeners
|
// Add listeners
|
||||||
//pepManager.addPEPListener(deviceListUpdateListener);
|
pepManager.addPEPListener(deviceListUpdateListener);
|
||||||
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
|
connection().addAsyncStanzaListener(internalOmemoMessageStanzaListener, omemoMessageStanzaFilter);
|
||||||
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
carbonManager.addCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
}
|
}
|
||||||
|
@ -820,7 +824,7 @@ public final class OmemoManager extends Manager {
|
||||||
* Remove active stanza listeners needed for OMEMO.
|
* Remove active stanza listeners needed for OMEMO.
|
||||||
*/
|
*/
|
||||||
public void stopListeners() {
|
public void stopListeners() {
|
||||||
//PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
PEPManager.getInstanceFor(connection()).removePEPListener(deviceListUpdateListener);
|
||||||
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
connection().removeAsyncStanzaListener(internalOmemoMessageStanzaListener);
|
||||||
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
CarbonManager.getInstanceFor(connection()).removeCarbonCopyReceivedListener(internalOmemoCarbonCopyListener);
|
||||||
}
|
}
|
||||||
|
@ -844,6 +848,9 @@ public final class OmemoManager extends Manager {
|
||||||
return service;
|
return service;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* StanzaListener that listens for incoming Stanzas which contain OMEMO elements.
|
||||||
|
*/
|
||||||
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
|
private final StanzaListener internalOmemoMessageStanzaListener = new StanzaListener() {
|
||||||
@Override
|
@Override
|
||||||
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
public void processStanza(Stanza packet) throws SmackException.NotConnectedException, InterruptedException {
|
||||||
|
@ -856,6 +863,9 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CarbonCopyListener that listens for incoming carbon copies which contain OMEMO elements.
|
||||||
|
*/
|
||||||
private final CarbonCopyReceivedListener internalOmemoCarbonCopyListener = new CarbonCopyReceivedListener() {
|
private final CarbonCopyReceivedListener internalOmemoCarbonCopyListener = new CarbonCopyReceivedListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onCarbonCopyReceived(CarbonExtension.Direction direction,
|
public void onCarbonCopyReceived(CarbonExtension.Direction direction,
|
||||||
|
@ -872,6 +882,64 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* PEPListener that listens for OMEMO deviceList updates.
|
||||||
|
*/
|
||||||
|
private final PEPListener deviceListUpdateListener = new PEPListener() {
|
||||||
|
@Override
|
||||||
|
public void eventReceived(EntityBareJid from, EventElement event, Message message) {
|
||||||
|
|
||||||
|
// Unknown sender, no more work to do.
|
||||||
|
if (from == null) {
|
||||||
|
// TODO: This DOES happen for some reason. Figure out when...
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ExtensionElement items : event.getExtensions()) {
|
||||||
|
if (!(items instanceof ItemsExtension)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (ExtensionElement item : ((ItemsExtension) items).getItems()) {
|
||||||
|
if (!(item instanceof PayloadItem<?>)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
PayloadItem<?> payloadItem = (PayloadItem<?>) item;
|
||||||
|
|
||||||
|
if (!(payloadItem.getPayload() instanceof OmemoDeviceListElement)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Device List <list>
|
||||||
|
OmemoDeviceListElement receivedDeviceList = (OmemoDeviceListElement) payloadItem.getPayload();
|
||||||
|
getOmemoService().getOmemoStoreBackend().mergeCachedDeviceList(getOwnDevice(), from, receivedDeviceList);
|
||||||
|
|
||||||
|
if (!from.asBareJid().equals(getOwnJid())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
OmemoCachedDeviceList deviceList = getOmemoService().cleanUpDeviceList(getOwnDevice());
|
||||||
|
final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList);
|
||||||
|
|
||||||
|
if (!newDeviceList.equals(receivedDeviceList)) {
|
||||||
|
Async.go(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
OmemoService.publishDeviceList(connection(), newDeviceList);
|
||||||
|
} catch (InterruptedException | XMPPException.XMPPErrorException |
|
||||||
|
SmackException.NotConnectedException | SmackException.NoResponseException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not publish our deviceList upon an received update.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* StanzaFilter that filters messages containing a OMEMO element.
|
* StanzaFilter that filters messages containing a OMEMO element.
|
||||||
*/
|
*/
|
||||||
|
@ -882,6 +950,9 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Guard class which ensures that the wrapped OmemoManager knows its BareJid.
|
||||||
|
*/
|
||||||
public static class LoggedInOmemoManager {
|
public static class LoggedInOmemoManager {
|
||||||
|
|
||||||
private final OmemoManager manager;
|
private final OmemoManager manager;
|
||||||
|
@ -909,13 +980,23 @@ public final class OmemoManager extends Manager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface FinishedCallback {
|
/**
|
||||||
|
* Callback which can be used to get notified, when the OmemoManager finished initializing.
|
||||||
|
*/
|
||||||
|
public interface InitializationFinishedCallback {
|
||||||
|
|
||||||
void initializationFinished(OmemoManager manager);
|
void initializationFinished(OmemoManager manager);
|
||||||
|
|
||||||
void initializationFailed(Exception cause);
|
void initializationFailed(Exception cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the bareJid of the user from the authenticated XMPP connection.
|
||||||
|
* If our deviceId is unknown, use the bareJid to look up deviceIds available in the omemoStore.
|
||||||
|
* If there are ids available, choose the smallest one. Otherwise generate a random deviceId.
|
||||||
|
*
|
||||||
|
* @param manager OmemoManager
|
||||||
|
*/
|
||||||
private static void initBareJidAndDeviceId(OmemoManager manager) {
|
private static void initBareJidAndDeviceId(OmemoManager manager) {
|
||||||
if (!manager.getConnection().isAuthenticated()) {
|
if (!manager.getConnection().isAuthenticated()) {
|
||||||
throw new IllegalStateException("Connection MUST be authenticated.");
|
throw new IllegalStateException("Connection MUST be authenticated.");
|
||||||
|
|
|
@ -34,21 +34,32 @@ import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||||
public class OmemoMessage {
|
public class OmemoMessage {
|
||||||
|
|
||||||
private final OmemoElement element;
|
private final OmemoElement element;
|
||||||
|
private final byte[] messageKey, iv;
|
||||||
|
|
||||||
OmemoMessage(OmemoElement element) {
|
OmemoMessage(OmemoElement element, byte[] key, byte[] iv) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
|
this.messageKey = key;
|
||||||
|
this.iv = iv;
|
||||||
}
|
}
|
||||||
|
|
||||||
public OmemoElement getElement() {
|
public OmemoElement getElement() {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] getKey() {
|
||||||
|
return messageKey.clone();
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getIv() {
|
||||||
|
return iv.clone();
|
||||||
|
}
|
||||||
|
|
||||||
public static class Sent extends OmemoMessage {
|
public static class Sent extends OmemoMessage {
|
||||||
private final ArrayList<OmemoDevice> intendedDevices = new ArrayList<>();
|
private final ArrayList<OmemoDevice> intendedDevices = new ArrayList<>();
|
||||||
private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
|
private final HashMap<OmemoDevice, Throwable> skippedDevices = new HashMap<>();
|
||||||
|
|
||||||
Sent(OmemoElement element, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
|
Sent(OmemoElement element, byte[] key, byte[] iv, List<OmemoDevice> intendedDevices, HashMap<OmemoDevice, Throwable> skippedDevices) {
|
||||||
super(element);
|
super(element, key, iv);
|
||||||
this.intendedDevices.addAll(intendedDevices);
|
this.intendedDevices.addAll(intendedDevices);
|
||||||
this.skippedDevices.putAll(skippedDevices);
|
this.skippedDevices.putAll(skippedDevices);
|
||||||
}
|
}
|
||||||
|
@ -86,13 +97,15 @@ public class OmemoMessage {
|
||||||
private final OmemoFingerprint sendersFingerprint;
|
private final OmemoFingerprint sendersFingerprint;
|
||||||
private final OmemoDevice senderDevice;
|
private final OmemoDevice senderDevice;
|
||||||
private final CARBON carbon;
|
private final CARBON carbon;
|
||||||
|
private final boolean preKeyMessage;
|
||||||
|
|
||||||
Received(OmemoElement element, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon) {
|
Received(OmemoElement element, byte[] key, byte[] iv, String message, OmemoFingerprint sendersFingerprint, OmemoDevice senderDevice, CARBON carbon, boolean preKeyMessage) {
|
||||||
super(element);
|
super(element, key, iv);
|
||||||
this.message = message;
|
this.message = message;
|
||||||
this.sendersFingerprint = sendersFingerprint;
|
this.sendersFingerprint = sendersFingerprint;
|
||||||
this.senderDevice = senderDevice;
|
this.senderDevice = senderDevice;
|
||||||
this.carbon = carbon;
|
this.carbon = carbon;
|
||||||
|
this.preKeyMessage = preKeyMessage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMessage() {
|
public String getMessage() {
|
||||||
|
@ -115,6 +128,10 @@ public class OmemoMessage {
|
||||||
public CARBON getCarbon() {
|
public CARBON getCarbon() {
|
||||||
return carbon;
|
return carbon;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isPreKeyMessage() {
|
||||||
|
return preKeyMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.util.logging.Logger;
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
import javax.crypto.IllegalBlockSizeException;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
|
||||||
import org.jivesoftware.smack.util.StringUtils;
|
import org.jivesoftware.smack.util.StringUtils;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoKeyElement;
|
||||||
|
@ -43,16 +42,41 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
protected final OmemoManager omemoManager;
|
protected final OmemoManager omemoManager;
|
||||||
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor.
|
||||||
|
*
|
||||||
|
* @param omemoManager omemoManager
|
||||||
|
* @param store omemoStore
|
||||||
|
*/
|
||||||
public OmemoRatchet(OmemoManager omemoManager,
|
public OmemoRatchet(OmemoManager omemoManager,
|
||||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store) {
|
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store) {
|
||||||
this.omemoManager = omemoManager;
|
this.omemoManager = omemoManager;
|
||||||
this.store = store;
|
this.store = store;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrypt a double-ratchet-encrypted message key.
|
||||||
|
*
|
||||||
|
* @param sender sender of the message.
|
||||||
|
* @param encryptedKey key encrypted with the ratchet of the sender.
|
||||||
|
* @return decrypted message key.
|
||||||
|
*
|
||||||
|
* @throws CorruptedOmemoKeyException
|
||||||
|
* @throws NoRawSessionException
|
||||||
|
* @throws CryptoFailedException
|
||||||
|
* @throws UntrustedOmemoIdentityException
|
||||||
|
*/
|
||||||
public abstract byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey)
|
public abstract byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey)
|
||||||
throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException,
|
throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException,
|
||||||
UntrustedOmemoIdentityException;
|
UntrustedOmemoIdentityException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encrypt a messageKey with the double ratchet session of the recipient.
|
||||||
|
*
|
||||||
|
* @param recipient recipient of the message.
|
||||||
|
* @param messageKey key we want to encrypt.
|
||||||
|
* @return encrypted message key.
|
||||||
|
*/
|
||||||
public abstract CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey);
|
public abstract CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,11 +94,14 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||||
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
List<OmemoKeyElement> keys = element.getHeader().getKeys();
|
||||||
|
|
||||||
|
boolean preKey = false;
|
||||||
|
|
||||||
// Find key with our ID.
|
// Find key with our ID.
|
||||||
for (OmemoKeyElement k : keys) {
|
for (OmemoKeyElement k : keys) {
|
||||||
if (k.getId() == keyId) {
|
if (k.getId() == keyId) {
|
||||||
try {
|
try {
|
||||||
unpackedKey = doubleRatchetDecrypt(sender, k.getData());
|
unpackedKey = doubleRatchetDecrypt(sender, k.getData());
|
||||||
|
preKey = k.isPreKey();
|
||||||
break;
|
break;
|
||||||
} catch (CryptoFailedException e) {
|
} catch (CryptoFailedException e) {
|
||||||
// There might be multiple keys with our id, but we can only decrypt one.
|
// There might be multiple keys with our id, but we can only decrypt one.
|
||||||
|
@ -114,7 +141,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
+ unpackedKey.length + ". Probably legacy auth tag format.");
|
+ unpackedKey.length + ". Probably legacy auth tag format.");
|
||||||
}
|
}
|
||||||
|
|
||||||
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag);
|
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag, preKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -123,10 +150,12 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
*
|
*
|
||||||
* @param element omemoElement containing a payload.
|
* @param element omemoElement containing a payload.
|
||||||
* @param cipherAndAuthTag cipher and authentication tag.
|
* @param cipherAndAuthTag cipher and authentication tag.
|
||||||
* @return Message containing the decrypted payload in its body.
|
* @return decrypted plain text.
|
||||||
* @throws CryptoFailedException
|
* @throws CryptoFailedException if decryption using AES key fails.
|
||||||
*/
|
*/
|
||||||
static Message decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag) throws CryptoFailedException {
|
static String decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag)
|
||||||
|
throws CryptoFailedException
|
||||||
|
{
|
||||||
if (!element.isMessageElement()) {
|
if (!element.isMessageElement()) {
|
||||||
throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
|
throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
|
||||||
}
|
}
|
||||||
|
@ -140,9 +169,7 @@ public abstract class OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
|
String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
|
||||||
Message decrypted = new Message();
|
return plaintext;
|
||||||
decrypted.setBody(plaintext);
|
|
||||||
return decrypted;
|
|
||||||
|
|
||||||
} catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
|
} catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
|
||||||
throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
|
throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
|
||||||
|
|
|
@ -32,6 +32,7 @@ import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.logging.Level;
|
import java.util.logging.Level;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import javax.crypto.BadPaddingException;
|
import javax.crypto.BadPaddingException;
|
||||||
|
@ -45,17 +46,23 @@ import org.jivesoftware.smack.XMPPException;
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Message;
|
||||||
import org.jivesoftware.smack.packet.Stanza;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||||
|
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||||
|
import org.jivesoftware.smackx.muc.MultiUserChatManager;
|
||||||
|
import org.jivesoftware.smackx.muc.Occupant;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement_VAxolotl;
|
||||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||||
|
import org.jivesoftware.smackx.omemo.element.OmemoElement_VAxolotl;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
|
import org.jivesoftware.smackx.omemo.exceptions.NoIdentityKeyException;
|
||||||
|
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.StaleDeviceException;
|
import org.jivesoftware.smackx.omemo.exceptions.StaleDeviceException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
import org.jivesoftware.smackx.omemo.exceptions.UndecidedOmemoIdentityException;
|
||||||
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
||||||
|
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
import org.jivesoftware.smackx.omemo.internal.OmemoCachedDeviceList;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||||
import org.jivesoftware.smackx.omemo.internal.listener.OmemoCarbonCopyStanzaReceivedListener;
|
import org.jivesoftware.smackx.omemo.internal.listener.OmemoCarbonCopyStanzaReceivedListener;
|
||||||
|
@ -72,6 +79,8 @@ import org.jivesoftware.smackx.pubsub.PubSubManager;
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
import org.jxmpp.jid.BareJid;
|
import org.jxmpp.jid.BareJid;
|
||||||
|
import org.jxmpp.jid.EntityBareJid;
|
||||||
|
import org.jxmpp.jid.Jid;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class contains OMEMO related logic and registers listeners etc.
|
* This class contains OMEMO related logic and registers listeners etc.
|
||||||
|
@ -103,8 +112,8 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
*/
|
*/
|
||||||
private static OmemoService<?, ?, ?, ?, ?, ?, ?, ?, ?> INSTANCE;
|
private static OmemoService<?, ?, ?, ?, ?, ?, ?, ?, ?> INSTANCE;
|
||||||
|
|
||||||
protected OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
private OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
||||||
protected final HashMap<OmemoManager, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>> omemoRatchets = new HashMap<>();
|
private final HashMap<OmemoManager, OmemoRatchet<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>> omemoRatchets = new HashMap<>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new OmemoService object. This should only happen once.
|
* Create a new OmemoService object. This should only happen once.
|
||||||
|
@ -120,10 +129,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @throws BadPaddingException when cipher.doFinal gets wrong padding
|
* @throws BadPaddingException when cipher.doFinal gets wrong padding
|
||||||
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size.
|
* @throws IllegalBlockSizeException when cipher.doFinal gets wrong Block size.
|
||||||
*/
|
*/
|
||||||
protected OmemoService()
|
protected OmemoService() {
|
||||||
throws NoSuchPaddingException, InvalidKeyException, UnsupportedEncodingException, IllegalBlockSizeException,
|
|
||||||
BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException
|
|
||||||
{
|
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,6 +184,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
*
|
*
|
||||||
* @param omemoStore store.
|
* @param omemoStore store.
|
||||||
*/
|
*/
|
||||||
|
@SuppressWarnings("unused")
|
||||||
public void setOmemoStoreBackend(
|
public void setOmemoStoreBackend(
|
||||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore) {
|
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore) {
|
||||||
if (this.omemoStore != null) {
|
if (this.omemoStore != null) {
|
||||||
|
@ -342,11 +349,11 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @throws UndecidedOmemoIdentityException if the list of recipient devices contains undecided devices
|
* @throws UndecidedOmemoIdentityException if the list of recipient devices contains undecided devices
|
||||||
* @throws CryptoFailedException if we are lacking some crypto primitives
|
* @throws CryptoFailedException if we are lacking some crypto primitives
|
||||||
*/
|
*/
|
||||||
OmemoMessage.Sent encrypt(OmemoManager.LoggedInOmemoManager managerGuard,
|
private OmemoMessage.Sent encrypt(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||||
List<OmemoDevice> contactsDevices,
|
List<OmemoDevice> contactsDevices,
|
||||||
byte[] messageKey,
|
byte[] messageKey,
|
||||||
byte[] iv,
|
byte[] iv,
|
||||||
String message)
|
String message)
|
||||||
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
throws SmackException.NotConnectedException, InterruptedException, SmackException.NoResponseException,
|
||||||
UndecidedOmemoIdentityException, CryptoFailedException
|
UndecidedOmemoIdentityException, CryptoFailedException
|
||||||
{
|
{
|
||||||
|
@ -418,23 +425,72 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
|
|
||||||
OmemoElement element = builder.finish();
|
OmemoElement element = builder.finish();
|
||||||
|
|
||||||
return new OmemoMessage.Sent(element, contactsDevices, skippedRecipients);
|
return new OmemoMessage.Sent(element, messageKey, iv, contactsDevices, skippedRecipients);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* Decrypt an OMEMO message.
|
||||||
|
* @param managerGuard authenticated OmemoManager.
|
||||||
|
* @param senderJid BareJid of the sender.
|
||||||
|
* @param omemoElement omemoElement.
|
||||||
|
* @return decrypted OmemoMessage object.
|
||||||
|
*
|
||||||
|
* @throws CorruptedOmemoKeyException if the identityKey of the sender is damaged.
|
||||||
|
* @throws CryptoFailedException if decryption fails.
|
||||||
|
* @throws NoRawSessionException if we have no session with the device and it sent a normal (non-preKey) message.
|
||||||
|
*/
|
||||||
|
private OmemoMessage.Received decryptMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||||
|
BareJid senderJid,
|
||||||
|
OmemoElement omemoElement,
|
||||||
|
OmemoMessage.CARBON carbon)
|
||||||
|
throws CorruptedOmemoKeyException, CryptoFailedException, NoRawSessionException
|
||||||
|
{
|
||||||
|
OmemoManager manager = managerGuard.get();
|
||||||
|
int senderId = omemoElement.getHeader().getSid();
|
||||||
|
OmemoDevice senderDevice = new OmemoDevice(senderJid, senderId);
|
||||||
|
|
||||||
|
CipherAndAuthTag cipherAndAuthTag = getOmemoRatchet(manager)
|
||||||
|
.retrieveMessageKeyAndAuthTag(senderDevice, omemoElement);
|
||||||
|
|
||||||
|
// Retrieve senders fingerprint. TODO: Find a way to do this without the store.
|
||||||
|
OmemoFingerprint senderFingerprint;
|
||||||
|
try {
|
||||||
|
senderFingerprint = getOmemoStoreBackend().getFingerprint(manager.getOwnDevice(), senderDevice);
|
||||||
|
} catch (NoIdentityKeyException e) {
|
||||||
|
throw new AssertionError("Cannot retrieve OmemoFingerprint of sender although decryption was successful: " + e);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (omemoElement.isMessageElement()) {
|
||||||
|
// Use symmetric message key to decrypt message payload.
|
||||||
|
String plaintext = OmemoRatchet.decryptMessageElement(omemoElement, cipherAndAuthTag);
|
||||||
|
|
||||||
|
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||||
|
plaintext, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||||
|
|
||||||
|
} else {
|
||||||
|
// KeyTransportMessages don't require decryption of the payload.
|
||||||
|
return new OmemoMessage.Received(omemoElement, cipherAndAuthTag.getKey(), cipherAndAuthTag.getIv(),
|
||||||
|
null, senderFingerprint, senderDevice, carbon, cipherAndAuthTag.wasPreKeyEncrypted());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create an OMEMO KeyTransportElement.
|
||||||
|
* @see <a href="https://xmpp.org/extensions/xep-0384.html#usecases-keysend">XEP-0384: Sending a key</a>.
|
||||||
|
*
|
||||||
|
* @param managerGuard Initialized OmemoManager.
|
||||||
|
* @param contactsDevices recipient devices.
|
||||||
|
* @param key AES-Key to be transported.
|
||||||
|
* @param iv initialization vector to be used with the key.
|
||||||
|
* @return KeyTransportElement
|
||||||
*
|
*
|
||||||
* @param managerGuard
|
|
||||||
* @param contactsDevices
|
|
||||||
* @param key
|
|
||||||
* @param iv
|
|
||||||
* @return
|
|
||||||
* @throws InterruptedException
|
* @throws InterruptedException
|
||||||
* @throws UndecidedOmemoIdentityException if the list of recipients contains an undecided device
|
* @throws UndecidedOmemoIdentityException if the list of recipients contains an undecided device
|
||||||
* @throws CryptoFailedException if we are lacking some cryptographic algorithms
|
* @throws CryptoFailedException if we are lacking some cryptographic algorithms
|
||||||
* @throws SmackException.NotConnectedException
|
* @throws SmackException.NotConnectedException
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
*/
|
*/
|
||||||
OmemoMessage.Sent createKeyTransportMessage(OmemoManager.LoggedInOmemoManager managerGuard,
|
OmemoMessage.Sent createKeyTransportElement(OmemoManager.LoggedInOmemoManager managerGuard,
|
||||||
List<OmemoDevice> contactsDevices,
|
List<OmemoDevice> contactsDevices,
|
||||||
byte[] key,
|
byte[] key,
|
||||||
byte[] iv)
|
byte[] iv)
|
||||||
|
@ -570,14 +626,24 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @throws SmackException.NoResponseException
|
* @throws SmackException.NoResponseException
|
||||||
* @throws PubSubException.NotALeafNodeException
|
* @throws PubSubException.NotALeafNodeException
|
||||||
*/
|
*/
|
||||||
private static void publishDeviceList(XMPPConnection connection, OmemoDeviceListElement deviceList)
|
static void publishDeviceList(XMPPConnection connection, OmemoDeviceListElement deviceList)
|
||||||
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
throws InterruptedException, XMPPException.XMPPErrorException, SmackException.NotConnectedException,
|
||||||
SmackException.NoResponseException, PubSubException.NotALeafNodeException
|
SmackException.NoResponseException
|
||||||
{
|
{
|
||||||
PubSubManager.getInstance(connection, connection.getUser().asBareJid())
|
PubSubManager.getInstance(connection, connection.getUser().asBareJid())
|
||||||
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
|
.tryToPublishAndPossibleAutoCreate(OmemoConstants.PEP_NODE_DEVICE_LIST, new PayloadItem<>(deviceList));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param connection
|
||||||
|
* @param userDevice
|
||||||
|
* @throws InterruptedException
|
||||||
|
* @throws PubSubException.NotALeafNodeException
|
||||||
|
* @throws XMPPException.XMPPErrorException
|
||||||
|
* @throws SmackException.NotConnectedException
|
||||||
|
* @throws SmackException.NoResponseException
|
||||||
|
*/
|
||||||
private void refreshAndRepublishDeviceList(XMPPConnection connection, OmemoDevice userDevice)
|
private void refreshAndRepublishDeviceList(XMPPConnection connection, OmemoDevice userDevice)
|
||||||
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
throws InterruptedException, PubSubException.NotALeafNodeException, XMPPException.XMPPErrorException,
|
||||||
SmackException.NotConnectedException, SmackException.NoResponseException
|
SmackException.NotConnectedException, SmackException.NoResponseException
|
||||||
|
@ -592,21 +658,10 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
if (publishedList == null) {
|
if (publishedList == null) {
|
||||||
publishedList = new OmemoDeviceListElement_VAxolotl(Collections.<Integer>emptySet());
|
publishedList = new OmemoDeviceListElement_VAxolotl(Collections.<Integer>emptySet());
|
||||||
}
|
}
|
||||||
OmemoCachedDeviceList cachedList = getOmemoStoreBackend().mergeCachedDeviceList(
|
|
||||||
userDevice, userDevice.getJid(), publishedList);
|
|
||||||
|
|
||||||
|
getOmemoStoreBackend().mergeCachedDeviceList(userDevice, userDevice.getJid(), publishedList);
|
||||||
|
|
||||||
// Delete stale devices if allowed and necessary
|
OmemoCachedDeviceList cachedList = cleanUpDeviceList(userDevice);
|
||||||
if (OmemoConfiguration.getDeleteStaleDevices()) {
|
|
||||||
cachedList = deleteStaleDevices(userDevice);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add back our device if necessary
|
|
||||||
if (!cachedList.getActiveDevices().contains(userDevice.getDeviceId())) {
|
|
||||||
cachedList.addDevice(userDevice.getDeviceId());
|
|
||||||
}
|
|
||||||
|
|
||||||
getOmemoStoreBackend().storeCachedDeviceList(userDevice, userDevice.getJid(), cachedList);
|
|
||||||
|
|
||||||
// Republish our deviceId if it is missing from the published list.
|
// Republish our deviceId if it is missing from the published list.
|
||||||
if (!publishedList.getDeviceIds().equals(cachedList.getActiveDevices())) {
|
if (!publishedList.getDeviceIds().equals(cachedList.getActiveDevices())) {
|
||||||
|
@ -614,6 +669,33 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Add our load the deviceList of the user from cache, delete stale devices if needed, add the users device
|
||||||
|
* back if necessary, store the refurbished list in cache and return it.
|
||||||
|
*
|
||||||
|
* @param userDevice
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
OmemoCachedDeviceList cleanUpDeviceList(OmemoDevice userDevice) {
|
||||||
|
OmemoCachedDeviceList cachedDeviceList;
|
||||||
|
|
||||||
|
// Delete stale devices if allowed and necessary
|
||||||
|
if (OmemoConfiguration.getDeleteStaleDevices()) {
|
||||||
|
cachedDeviceList = deleteStaleDevices(userDevice);
|
||||||
|
} else {
|
||||||
|
cachedDeviceList = getOmemoStoreBackend().loadCachedDeviceList(userDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Add back our device if necessary
|
||||||
|
if (!cachedDeviceList.getActiveDevices().contains(userDevice.getDeviceId())) {
|
||||||
|
cachedDeviceList.addDevice(userDevice.getDeviceId());
|
||||||
|
}
|
||||||
|
|
||||||
|
getOmemoStoreBackend().storeCachedDeviceList(userDevice, userDevice.getJid(), cachedDeviceList);
|
||||||
|
return cachedDeviceList;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Refresh and merge device list of contact.
|
* Refresh and merge device list of contact.
|
||||||
*
|
*
|
||||||
|
@ -831,7 +913,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @param contactsDevice OmemoDevice of the contact.
|
* @param contactsDevice OmemoDevice of the contact.
|
||||||
* @return true if userDevice has session with contactsDevice.
|
* @return true if userDevice has session with contactsDevice.
|
||||||
*/
|
*/
|
||||||
boolean hasSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
private boolean hasSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||||
return getOmemoStoreBackend().loadRawSession(userDevice, contactsDevice) != null;
|
return getOmemoStoreBackend().loadRawSession(userDevice, contactsDevice) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -948,7 +1030,7 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
* @param userDevice our OmemoDevice
|
* @param userDevice our OmemoDevice
|
||||||
* @param devices collection of OmemoDevices
|
* @param devices collection of OmemoDevices
|
||||||
*/
|
*/
|
||||||
static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
private static void removeOurDevice(OmemoDevice userDevice, Collection<OmemoDevice> devices) {
|
||||||
if (devices.contains(userDevice)) {
|
if (devices.contains(userDevice)) {
|
||||||
devices.remove(userDevice);
|
devices.remove(userDevice);
|
||||||
}
|
}
|
||||||
|
@ -996,7 +1078,74 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager omemoManager) {
|
public void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.LoggedInOmemoManager managerGuard) {
|
||||||
// TODO: Implement
|
OmemoManager manager = managerGuard.get();
|
||||||
|
OmemoElement element = stanza.getExtension(OmemoElement.NAME_ENCRYPTED, OmemoElement_VAxolotl.NAMESPACE);
|
||||||
|
if (element == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
OmemoMessage.Received decrypted;
|
||||||
|
|
||||||
|
MultiUserChat muc = getMuc(manager.getConnection(), stanza.getFrom());
|
||||||
|
if (muc != null) {
|
||||||
|
Occupant occupant = muc.getOccupant(stanza.getFrom().asEntityFullJidIfPossible());
|
||||||
|
Jid occupantJid = occupant.getJid();
|
||||||
|
|
||||||
|
if (occupantJid == null) {
|
||||||
|
LOGGER.log(Level.WARNING, "MUC message received, but there is no way to retrieve the senders Jid. " +
|
||||||
|
stanza.getFrom());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
BareJid sender = occupantJid.asBareJid();
|
||||||
|
try {
|
||||||
|
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
||||||
|
|
||||||
|
if (element.isMessageElement()) {
|
||||||
|
manager.notifyOmemoMucMessageReceived(muc, stanza, decrypted);
|
||||||
|
} else {
|
||||||
|
manager.notifyOmemoMucKeyTransportMessageReceived(muc, stanza, decrypted);
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch (CorruptedOmemoKeyException | CryptoFailedException | NoRawSessionException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
BareJid sender = stanza.getFrom().asBareJid();
|
||||||
|
try {
|
||||||
|
decrypted = decryptMessage(managerGuard, sender, element, OmemoMessage.CARBON.NONE);
|
||||||
|
|
||||||
|
if (element.isMessageElement()) {
|
||||||
|
manager.notifyOmemoMessageReceived(stanza, decrypted);
|
||||||
|
} else {
|
||||||
|
manager.notifyOmemoKeyTransportMessageReceived(stanza, decrypted);
|
||||||
|
}
|
||||||
|
} catch (CorruptedOmemoKeyException | CryptoFailedException | NoRawSessionException e) {
|
||||||
|
LOGGER.log(Level.WARNING, "Could not decrypt incoming message: ", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return the joined MUC with EntityBareJid jid, or null if its not a room and/or not joined.
|
||||||
|
* @param connection xmpp connection
|
||||||
|
* @param jid jid (presumably) of the MUC
|
||||||
|
* @return MultiUserChat or null if not a MUC.
|
||||||
|
*/
|
||||||
|
private static MultiUserChat getMuc(XMPPConnection connection, Jid jid) {
|
||||||
|
EntityBareJid ebj = jid.asEntityBareJidIfPossible();
|
||||||
|
if (ebj == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
MultiUserChatManager mucm = MultiUserChatManager.getInstanceFor(connection);
|
||||||
|
Set<EntityBareJid> joinedRooms = mucm.getJoinedRooms();
|
||||||
|
if (joinedRooms.contains(ebj)) {
|
||||||
|
return mucm.getMultiUserChat(ebj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,8 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.OMEMO_NAMESPACE_
|
||||||
*/
|
*/
|
||||||
public class OmemoElement_VAxolotl extends OmemoElement {
|
public class OmemoElement_VAxolotl extends OmemoElement {
|
||||||
|
|
||||||
|
public static final String NAMESPACE = OMEMO_NAMESPACE_V_AXOLOTL;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a new OmemoMessageElement from a header and a payload.
|
* Create a new OmemoMessageElement from a header and a payload.
|
||||||
*
|
*
|
||||||
|
@ -37,6 +39,6 @@ public class OmemoElement_VAxolotl extends OmemoElement {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getNamespace() {
|
public String getNamespace() {
|
||||||
return OMEMO_NAMESPACE_V_AXOLOTL;
|
return NAMESPACE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,19 @@
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Copyright 2017 Paul Schaub
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.element;
|
package org.jivesoftware.smackx.omemo.element;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.NamedElement;
|
import org.jivesoftware.smack.packet.NamedElement;
|
||||||
|
|
|
@ -78,4 +78,16 @@ public class UntrustedOmemoIdentityException extends Exception {
|
||||||
public OmemoFingerprint getUntrustedFingerprint() {
|
public OmemoFingerprint getUntrustedFingerprint() {
|
||||||
return untrustedKey;
|
return untrustedKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
if (trustedKey != null) {
|
||||||
|
return "Untrusted OMEMO Identity encountered:\n" +
|
||||||
|
"Fingerprint of trusted key:\n" + trustedKey.blocksOf8Chars() + "\n" +
|
||||||
|
"Fingerprint of untrusted key:\n" + untrustedKey.blocksOf8Chars();
|
||||||
|
} else {
|
||||||
|
return "Untrusted OMEMO Identity encountered:\n" +
|
||||||
|
"Fingerprint of untrusted key:\n" + untrustedKey.blocksOf8Chars();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,7 +23,6 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.NoSuchProviderException;
|
import java.security.NoSuchProviderException;
|
||||||
|
|
||||||
import javax.crypto.Cipher;
|
import javax.crypto.Cipher;
|
||||||
import javax.crypto.NoSuchPaddingException;
|
import javax.crypto.NoSuchPaddingException;
|
||||||
import javax.crypto.spec.IvParameterSpec;
|
import javax.crypto.spec.IvParameterSpec;
|
||||||
|
@ -38,11 +37,13 @@ import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||||
*/
|
*/
|
||||||
public class CipherAndAuthTag {
|
public class CipherAndAuthTag {
|
||||||
private final byte[] key, iv, authTag;
|
private final byte[] key, iv, authTag;
|
||||||
|
private final boolean wasPreKey;
|
||||||
|
|
||||||
public CipherAndAuthTag(byte[] key, byte[] iv, byte[] authTag) throws CryptoFailedException {
|
public CipherAndAuthTag(byte[] key, byte[] iv, byte[] authTag, boolean wasPreKey) {
|
||||||
this.authTag = authTag;
|
this.authTag = authTag;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.iv = iv;
|
this.iv = iv;
|
||||||
|
this.wasPreKey = wasPreKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Cipher getCipher() throws CryptoFailedException {
|
public Cipher getCipher() throws CryptoFailedException {
|
||||||
|
@ -82,4 +83,8 @@ public class CipherAndAuthTag {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean wasPreKeyEncrypted() {
|
||||||
|
return wasPreKey;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,10 +16,8 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.listener;
|
package org.jivesoftware.smackx.omemo.listener;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener interface that allows implementations to receive decrypted OMEMO messages.
|
* Listener interface that allows implementations to receive decrypted OMEMO messages.
|
||||||
|
@ -30,20 +28,16 @@ public interface OmemoMessageListener {
|
||||||
/**
|
/**
|
||||||
* Gets called, whenever an OmemoMessage has been received and was successfully decrypted.
|
* Gets called, whenever an OmemoMessage has been received and was successfully decrypted.
|
||||||
*
|
*
|
||||||
* @param decryptedBody Decrypted body
|
* @param stanza Received (encrypted) stanza.
|
||||||
* @param encryptedMessage Encrypted Message
|
* @param decryptedMessage decrypted OmemoMessage.
|
||||||
* @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);
|
void onOmemoMessageReceived(Stanza stanza, OmemoMessage.Received decryptedMessage);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
||||||
*
|
*
|
||||||
* @param cipherAndAuthTag transported Cipher along with an optional AuthTag
|
* @param stanza received (encrypted) stanza.
|
||||||
* @param message Message that contained the KeyTransport
|
* @param decryptedKeyTransportMessage decrypted OMEMO key transport message.
|
||||||
* @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);
|
void onOmemoKeyTransportReceived(Stanza stanza, OmemoMessage.Received decryptedKeyTransportMessage);
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,13 +16,9 @@
|
||||||
*/
|
*/
|
||||||
package org.jivesoftware.smackx.omemo.listener;
|
package org.jivesoftware.smackx.omemo.listener;
|
||||||
|
|
||||||
import org.jivesoftware.smack.packet.Message;
|
import org.jivesoftware.smack.packet.Stanza;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.muc.MultiUserChat;
|
import org.jivesoftware.smackx.muc.MultiUserChat;
|
||||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
import org.jivesoftware.smackx.omemo.OmemoMessage;
|
||||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
|
||||||
|
|
||||||
import org.jxmpp.jid.BareJid;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Listener interface that allows implementations to receive decrypted OMEMO MUC messages.
|
* Listener interface that allows implementations to receive decrypted OMEMO MUC messages.
|
||||||
|
@ -33,24 +29,17 @@ public interface OmemoMucMessageListener {
|
||||||
/**
|
/**
|
||||||
* Gets called whenever an OMEMO message has been received in a MultiUserChat and successfully decrypted.
|
* Gets called whenever an OMEMO message has been received in a MultiUserChat and successfully decrypted.
|
||||||
* @param muc MultiUserChat the message was sent in
|
* @param muc MultiUserChat the message was sent in
|
||||||
* @param from the bareJid of the sender
|
* @param stanza Original Stanza
|
||||||
* @param decryptedBody the decrypted Body of the message
|
* @param decryptedOmemoMessage decrypted Omemo 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,
|
void onOmemoMucMessageReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedOmemoMessage);
|
||||||
Message wrappingMessage, OmemoMessageInformation omemoInformation);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
* Gets called, whenever an OmemoElement without a body (an OmemoKeyTransportElement) is received.
|
||||||
*
|
*
|
||||||
* @param muc MultiUserChat the message was sent in
|
* @param muc MultiUserChat the message was sent in
|
||||||
* @param from bareJid of the sender
|
* @param stanza original stanza
|
||||||
* @param cipherAndAuthTag transportedKey along with an optional authTag
|
* @param decryptedKeyTransportElement decrypted KeyTransportMessage
|
||||||
* @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);
|
void onOmemoKeyTransportReceived(MultiUserChat muc, Stanza stanza, OmemoMessage.Received decryptedKeyTransportElement);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,8 +21,6 @@ import static org.junit.Assert.assertNull;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.jivesoftware.smackx.omemo.OmemoConfiguration;
|
|
||||||
|
|
||||||
import junit.framework.TestCase;
|
import junit.framework.TestCase;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
|
@ -61,11 +61,12 @@ public class WrapperObjectsTest {
|
||||||
byte[] iv = OmemoMessageBuilder.generateIv();
|
byte[] iv = OmemoMessageBuilder.generateIv();
|
||||||
byte[] authTag = OmemoMessageBuilder.generateIv();
|
byte[] authTag = OmemoMessageBuilder.generateIv();
|
||||||
|
|
||||||
CipherAndAuthTag cat = new CipherAndAuthTag(key, iv, authTag);
|
CipherAndAuthTag cat = new CipherAndAuthTag(key, iv, authTag, true);
|
||||||
|
|
||||||
assertNotNull(cat.getCipher());
|
assertNotNull(cat.getCipher());
|
||||||
assertArrayEquals(key, cat.getKey());
|
assertArrayEquals(key, cat.getKey());
|
||||||
assertArrayEquals(iv, cat.getIv());
|
assertArrayEquals(iv, cat.getIv());
|
||||||
assertArrayEquals(authTag, cat.getAuthTag());
|
assertArrayEquals(authTag, cat.getAuthTag());
|
||||||
|
assertTrue(cat.wasPreKeyEncrypted());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue