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