mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-09 00:59:39 +02:00
Rework support for XEP-0384: OMEMO Encryption
Changes: Rework integration tests New structure of base integration test classes bump dependency on signal-protocol-java from 2.4.0 to 2.6.2 Introduced CachingOmemoStore implementations Use CachingOmemoStore classes in integration tests Removed OmemoSession classes (replaced with more logical OmemoRatchet classes) Consequently also removed load/storeOmemoSession methods from OmemoStore Removed some clutter from KeyUtil classes Moved trust decision related code from OmemoStore to TrustCallback Require authenticated connection for many functions Add async initialization function in OmemoStore Refactor omemo test package (/java/org/jivesoftware/smack/omemo -> /java/org/jivesoftware/smackx) Remove OmemoStore method isFreshInstallation() as well as defaultDeviceId related stuff FileBasedOmemoStore: Add cleaner methods to store/load base data types (Using tryWithResource, only for future releases, once Android API gets bumped) Attempt to make OmemoManager thread safe new logic for getInstanceFor() deviceId determination OmemoManagers encrypt methods now don't throw exceptions when encryption for some devices fails. Instead message gets encrypted when possible and more information about failures gets returned alongside the message itself Added OmemoMessage class for that purpose Reworked entire OmemoService class Use safer logic for creating trust-ignoring messages (like ratchet-update messages) Restructure elements/provider in order to prepare for OMEMO namespace bumps Remove OmemoManager.regenerate() methods in favor of getInstanceFor(connection, randomDeviceId) Removed some unnecessary configuration options Prepare for support of more AES message key types Simplify session creation Where possible, avoid side effects in methods Add UntrustedOmemoIdentityException Add TrustState enum More improved tests
This commit is contained in:
parent
f290197f6a
commit
1f731f6318
96 changed files with 6915 additions and 5488 deletions
|
@ -10,7 +10,8 @@ dependencies {
|
|||
compile project(":smack-im")
|
||||
compile project(":smack-extensions")
|
||||
compile project(":smack-omemo")
|
||||
compile 'org.whispersystems:signal-protocol-java:2.4.0'
|
||||
compile 'org.whispersystems:signal-protocol-java:2.6.2'
|
||||
|
||||
testCompile project(path: ":smack-core", configuration: "testRuntime")
|
||||
testCompile project(path: ":smack-omemo", configuration: "testRuntime")
|
||||
}
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.signal;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.CachingOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* Implementation of the CachingOmemoStore for smack-omemo-signal.
|
||||
* This Store implementation can either be used as a proxy wrapping a persistent SignalOmemoStore in order to prevent
|
||||
* excessive storage access, or it can be used standalone as an ephemeral store, which doesn't persist its contents.
|
||||
*/
|
||||
public class SignalCachingOmemoStore extends CachingOmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord,
|
||||
SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
/**
|
||||
* Create a new SignalCachingOmemoStore as a caching layer around a persisting OmemoStore
|
||||
* (eg. a SignalFileBasedOmemoStore).
|
||||
* @param wrappedStore other store implementation that gets wrapped
|
||||
*/
|
||||
public SignalCachingOmemoStore(OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord,
|
||||
SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> wrappedStore) {
|
||||
super(wrappedStore);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SignalCachingOmemoStore as an ephemeral standalone OmemoStore.
|
||||
*/
|
||||
public SignalCachingOmemoStore() {
|
||||
super(new SignalOmemoKeyUtil());
|
||||
}
|
||||
}
|
|
@ -42,18 +42,15 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
|||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class SignalFileBasedOmemoStore
|
||||
extends FileBasedOmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
public SignalFileBasedOmemoStore() {
|
||||
super();
|
||||
}
|
||||
extends FileBasedOmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
public SignalFileBasedOmemoStore(File base) {
|
||||
super(base);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> keyUtil() {
|
||||
public OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, ECPublicKey, PreKeyBundle> keyUtil() {
|
||||
return new SignalOmemoKeyUtil();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,25 +21,18 @@
|
|||
package org.jivesoftware.smackx.omemo.signal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoSession;
|
||||
import org.jivesoftware.smackx.omemo.trust.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
||||
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.ecc.Curve;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
|
@ -54,7 +47,7 @@ import org.whispersystems.libsignal.util.KeyHelper;
|
|||
* @author Paul Schaub
|
||||
*/
|
||||
public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord,
|
||||
SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
SessionRecord, ECPublicKey, PreKeyBundle> {
|
||||
|
||||
@Override
|
||||
public IdentityKeyPair generateOmemoIdentityKeyPair() {
|
||||
|
@ -62,17 +55,18 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
}
|
||||
|
||||
@Override
|
||||
public HashMap<Integer, PreKeyRecord> generateOmemoPreKeys(int currentPreKeyId, int count) {
|
||||
public TreeMap<Integer, PreKeyRecord> generateOmemoPreKeys(int currentPreKeyId, int count) {
|
||||
List<PreKeyRecord> preKeyRecords = KeyHelper.generatePreKeys(currentPreKeyId, count);
|
||||
HashMap<Integer, PreKeyRecord> hashMap = new HashMap<>();
|
||||
TreeMap<Integer, PreKeyRecord> map = new TreeMap<>();
|
||||
for (PreKeyRecord p : preKeyRecords) {
|
||||
hashMap.put(p.getId(), p);
|
||||
map.put(p.getId(), p);
|
||||
}
|
||||
return hashMap;
|
||||
return map;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignedPreKeyRecord generateOmemoSignedPreKey(IdentityKeyPair identityKeyPair, int currentPreKeyId) throws CorruptedOmemoKeyException {
|
||||
public SignedPreKeyRecord generateOmemoSignedPreKey(IdentityKeyPair identityKeyPair, int currentPreKeyId)
|
||||
throws CorruptedOmemoKeyException {
|
||||
try {
|
||||
return KeyHelper.generateSignedPreKey(identityKeyPair, currentPreKeyId);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -82,6 +76,7 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
|
||||
@Override
|
||||
public SignedPreKeyRecord signedPreKeyFromBytes(byte[] data) throws IOException {
|
||||
if (data == null) return null;
|
||||
return new SignedPreKeyRecord(data);
|
||||
}
|
||||
|
||||
|
@ -90,21 +85,9 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
return signedPreKeyRecord.serialize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmemoSession<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
createOmemoSession(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore,
|
||||
OmemoDevice contact, IdentityKey identityKey) {
|
||||
return new SignalOmemoSession(omemoManager, omemoStore, contact, identityKey);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmemoSession<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
createOmemoSession(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore, OmemoDevice from) {
|
||||
return new SignalOmemoSession(omemoManager, omemoStore, from);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionRecord rawSessionFromBytes(byte[] data) throws IOException {
|
||||
if (data == null) return null;
|
||||
return new SessionRecord(data);
|
||||
}
|
||||
|
||||
|
@ -115,6 +98,7 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
|
||||
@Override
|
||||
public IdentityKeyPair identityKeyPairFromBytes(byte[] data) throws CorruptedOmemoKeyException {
|
||||
if (data == null) return null;
|
||||
try {
|
||||
return new IdentityKeyPair(data);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -124,6 +108,7 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
|
||||
@Override
|
||||
public IdentityKey identityKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException {
|
||||
if (data == null) return null;
|
||||
try {
|
||||
return new IdentityKey(data, 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -133,6 +118,7 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
|
||||
@Override
|
||||
public ECPublicKey ellipticCurvePublicKeyFromBytes(byte[] data) throws CorruptedOmemoKeyException {
|
||||
if (data == null) return null;
|
||||
try {
|
||||
return Curve.decodePoint(data, 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
|
@ -147,11 +133,13 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
|
||||
@Override
|
||||
public PreKeyRecord preKeyFromBytes(byte[] bytes) throws IOException {
|
||||
if (bytes == null) return null;
|
||||
return new PreKeyRecord(bytes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreKeyBundle bundleFromOmemoBundle(OmemoBundleVAxolotlElement bundle, OmemoDevice contact, int preKeyId) throws CorruptedOmemoKeyException {
|
||||
public PreKeyBundle bundleFromOmemoBundle(OmemoBundleElement bundle, OmemoDevice contact, int preKeyId)
|
||||
throws CorruptedOmemoKeyException {
|
||||
return new PreKeyBundle(0,
|
||||
contact.getDeviceId(),
|
||||
preKeyId,
|
||||
|
@ -208,20 +196,23 @@ public class SignalOmemoKeyUtil extends OmemoKeyUtil<IdentityKeyPair, IdentityKe
|
|||
}
|
||||
|
||||
@Override
|
||||
public OmemoFingerprint getFingerprint(IdentityKey identityKey) {
|
||||
public OmemoFingerprint getFingerprintOfIdentityKey(IdentityKey identityKey) {
|
||||
if (identityKey == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String fp = identityKey.getFingerprint();
|
||||
// Cut "(byte)0x" prefixes, remove spaces and commas, cut first two digits.
|
||||
fp = fp.replace("(byte)0x", "").replace(",", "").replace(" ", "").substring(2);
|
||||
fp = fp.replace("(byte)0x", "").replace(",", "")
|
||||
.replace(" ", "").substring(2);
|
||||
return new OmemoFingerprint(fp);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignalProtocolAddress omemoDeviceAsAddress(OmemoDevice contact) {
|
||||
return new SignalProtocolAddress(contact.getJid().asBareJid().toString(), contact.getDeviceId());
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmemoDevice addressAsOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
|
||||
return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
|
||||
public OmemoFingerprint getFingerprintOfIdentityKeyPair(IdentityKeyPair identityKeyPair) {
|
||||
if (identityKeyPair == null) {
|
||||
return null;
|
||||
}
|
||||
return getFingerprintOfIdentityKey(identityKeyPair.getPublicKey());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,162 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.signal;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoRatchet;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
import org.whispersystems.libsignal.DuplicateMessageException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||
import org.whispersystems.libsignal.InvalidMessageException;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.libsignal.LegacyMessageException;
|
||||
import org.whispersystems.libsignal.NoSessionException;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.UntrustedIdentityException;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
|
||||
import org.whispersystems.libsignal.protocol.SignalMessage;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
public class SignalOmemoRatchet
|
||||
extends OmemoRatchet<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(OmemoRatchet.class.getName());
|
||||
private final SignalOmemoStoreConnector storeConnector;
|
||||
|
||||
SignalOmemoRatchet(OmemoManager omemoManager,
|
||||
OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord,
|
||||
SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle,
|
||||
SessionCipher> store) {
|
||||
super(omemoManager, store);
|
||||
this.storeConnector = new SignalOmemoStoreConnector(omemoManager, store);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey)
|
||||
throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException,
|
||||
UntrustedOmemoIdentityException {
|
||||
|
||||
SessionCipher cipher = getCipher(sender);
|
||||
byte[] decryptedKey;
|
||||
|
||||
// Try to handle the message as a PreKeySignalMessage...
|
||||
try {
|
||||
PreKeySignalMessage preKeyMessage = new PreKeySignalMessage(encryptedKey);
|
||||
|
||||
if (!preKeyMessage.getPreKeyId().isPresent()) {
|
||||
throw new CryptoFailedException("PreKeyMessage did not contain a preKeyId.");
|
||||
}
|
||||
|
||||
IdentityKey messageIdentityKey = preKeyMessage.getIdentityKey();
|
||||
IdentityKey previousIdentityKey = store.loadOmemoIdentityKey(storeConnector.getOurDevice(), sender);
|
||||
|
||||
if (previousIdentityKey != null &&
|
||||
!previousIdentityKey.getFingerprint().equals(messageIdentityKey.getFingerprint())) {
|
||||
throw new UntrustedOmemoIdentityException(sender,
|
||||
store.keyUtil().getFingerprintOfIdentityKey(previousIdentityKey),
|
||||
store.keyUtil().getFingerprintOfIdentityKey(messageIdentityKey));
|
||||
}
|
||||
|
||||
try {
|
||||
decryptedKey = cipher.decrypt(preKeyMessage);
|
||||
}
|
||||
catch (UntrustedIdentityException e) {
|
||||
throw new AssertionError("Signals trust management MUST be disabled.");
|
||||
}
|
||||
catch (LegacyMessageException | InvalidKeyException e) {
|
||||
throw new CryptoFailedException(e);
|
||||
}
|
||||
catch (InvalidKeyIdException e) {
|
||||
throw new NoRawSessionException(sender, e);
|
||||
}
|
||||
catch (DuplicateMessageException e) {
|
||||
LOGGER.log(Level.INFO, "Decryption of PreKeyMessage from " + sender +
|
||||
" failed, since the message has been decrypted before.");
|
||||
return null;
|
||||
}
|
||||
|
||||
} catch (InvalidVersionException | InvalidMessageException noPreKeyMessage) {
|
||||
// ...if that fails, handle it as a SignalMessage
|
||||
try {
|
||||
SignalMessage message = new SignalMessage(encryptedKey);
|
||||
decryptedKey = getCipher(sender).decrypt(message);
|
||||
}
|
||||
catch (UntrustedIdentityException e) {
|
||||
throw new AssertionError("Signals trust management MUST be disabled.");
|
||||
}
|
||||
catch (InvalidMessageException | NoSessionException e) {
|
||||
throw new NoRawSessionException(sender, e);
|
||||
}
|
||||
catch (LegacyMessageException e) {
|
||||
throw new CryptoFailedException(e);
|
||||
}
|
||||
catch (DuplicateMessageException e1) {
|
||||
LOGGER.log(Level.INFO, "Decryption of SignalMessage from " + sender +
|
||||
" failed, since the message has been decrypted before.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return decryptedKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey) {
|
||||
CiphertextMessage ciphertextMessage;
|
||||
try {
|
||||
ciphertextMessage = getCipher(recipient).encrypt(messageKey);
|
||||
} catch (UntrustedIdentityException e) {
|
||||
throw new AssertionError("Signals trust management MUST be disabled.");
|
||||
}
|
||||
|
||||
// TODO: Figure out, if this is enough...
|
||||
int type = (ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE ?
|
||||
OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE : OmemoElement.TYPE_OMEMO_MESSAGE);
|
||||
|
||||
return new CiphertextTuple(ciphertextMessage.serialize(), type);
|
||||
}
|
||||
|
||||
private SessionCipher getCipher(OmemoDevice device) {
|
||||
return new SessionCipher(storeConnector, storeConnector, storeConnector, storeConnector,
|
||||
SignalOmemoStoreConnector.asAddress(device));
|
||||
}
|
||||
}
|
|
@ -20,21 +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;
|
||||
|
@ -59,16 +46,27 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
|||
* @author Paul Schaub
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class SignalOmemoService extends OmemoService<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
public final class SignalOmemoService
|
||||
extends OmemoService<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
private static SignalOmemoService INSTANCE;
|
||||
private static boolean LICENSE_ACKNOWLEDGED = false;
|
||||
|
||||
public static void setup() throws InvalidKeyException, XMPPErrorException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException, SmackException, InterruptedException, CorruptedOmemoKeyException {
|
||||
@Override
|
||||
protected SignalOmemoRatchet instantiateOmemoRatchet(
|
||||
OmemoManager manager,
|
||||
OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> store) {
|
||||
|
||||
return new SignalOmemoRatchet(manager, getOmemoStoreBackend());
|
||||
}
|
||||
|
||||
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. See for example " +
|
||||
"https://www.gnu.org/licenses/quick-guide-gplv3 for more details. Please call " +
|
||||
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. " +
|
||||
"See for example https://www.gnu.org/licenses/quick-guide-gplv3 for more details. Please call " +
|
||||
"SignalOmemoService.acknowledgeLicense() prior to the setup() method in order to prevent " +
|
||||
"this exception.");
|
||||
}
|
||||
|
@ -79,15 +77,13 @@ public final class SignalOmemoService extends OmemoService<IdentityKeyPair, Iden
|
|||
}
|
||||
|
||||
@Override
|
||||
public OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> createDefaultOmemoStoreBackend() {
|
||||
return new SignalFileBasedOmemoStore();
|
||||
public OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
createDefaultOmemoStoreBackend() {
|
||||
return new SignalCachingOmemoStore();
|
||||
}
|
||||
|
||||
private SignalOmemoService()
|
||||
throws SmackException, InterruptedException, XMPPException.XMPPErrorException, CorruptedOmemoKeyException,
|
||||
NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException,
|
||||
IllegalBlockSizeException, BadPaddingException, NoSuchAlgorithmException, NoSuchProviderException,
|
||||
java.security.InvalidKeyException {
|
||||
private SignalOmemoService() {
|
||||
super();
|
||||
}
|
||||
|
||||
|
@ -96,14 +92,17 @@ public final class SignalOmemoService extends OmemoService<IdentityKeyPair, Iden
|
|||
}
|
||||
|
||||
@Override
|
||||
protected void processBundle(OmemoManager omemoManager, PreKeyBundle preKeyBundle, OmemoDevice contact) throws CorruptedOmemoKeyException {
|
||||
protected void processBundle(OmemoManager omemoManager,
|
||||
PreKeyBundle contactsBundle,
|
||||
OmemoDevice contactsDevice)
|
||||
throws CorruptedOmemoKeyException {
|
||||
|
||||
SignalOmemoStoreConnector connector = new SignalOmemoStoreConnector(omemoManager, getOmemoStoreBackend());
|
||||
SessionBuilder builder = new SessionBuilder(connector, connector, connector, connector,
|
||||
getOmemoStoreBackend().keyUtil().omemoDeviceAsAddress(contact));
|
||||
SignalOmemoStoreConnector.asAddress(contactsDevice));
|
||||
try {
|
||||
builder.process(preKeyBundle);
|
||||
LOGGER.log(Level.INFO, "Session built with " + contact);
|
||||
getOmemoStoreBackend().getOmemoSessionOf(omemoManager, contact); //method puts session in session map.
|
||||
builder.process(contactsBundle);
|
||||
LOGGER.log(Level.FINE, "Session built with " + contactsDevice);
|
||||
} catch (org.whispersystems.libsignal.InvalidKeyException e) {
|
||||
throw new CorruptedOmemoKeyException(e.getMessage());
|
||||
} catch (UntrustedIdentityException e) {
|
||||
|
|
|
@ -1,142 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.signal;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoSession;
|
||||
|
||||
import org.whispersystems.libsignal.DuplicateMessageException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.InvalidKeyException;
|
||||
import org.whispersystems.libsignal.InvalidKeyIdException;
|
||||
import org.whispersystems.libsignal.InvalidMessageException;
|
||||
import org.whispersystems.libsignal.InvalidVersionException;
|
||||
import org.whispersystems.libsignal.LegacyMessageException;
|
||||
import org.whispersystems.libsignal.NoSessionException;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.UntrustedIdentityException;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libsignal.protocol.PreKeySignalMessage;
|
||||
import org.whispersystems.libsignal.protocol.SignalMessage;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* Concrete implementation of the OmemoSession using the Signal library.
|
||||
*
|
||||
* @author Paul Schaub
|
||||
*/
|
||||
public class SignalOmemoSession extends OmemoSession<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
private static final Logger LOGGER = Logger.getLogger(SignalOmemoSession.class.getName());
|
||||
|
||||
/**
|
||||
* Constructor used when the remote user initialized the session using a PreKeyOmemoMessage.
|
||||
*
|
||||
* @param omemoManager omemoManager
|
||||
* @param omemoStore omemoStoreConnector that can be used to get information from
|
||||
* @param remoteContact omemoDevice of the remote contact
|
||||
* @param identityKey identityKey of the remote contact
|
||||
*/
|
||||
SignalOmemoSession(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore,
|
||||
OmemoDevice remoteContact, IdentityKey identityKey) {
|
||||
super(omemoManager, omemoStore, remoteContact, identityKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor used when we initiate a new Session with the remote user.
|
||||
*
|
||||
* @param omemoManager omemoManager
|
||||
* @param omemoStore omemoStore used to get information from
|
||||
* @param remoteContact omemoDevice of the remote contact
|
||||
*/
|
||||
SignalOmemoSession(OmemoManager omemoManager,
|
||||
OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> omemoStore,
|
||||
OmemoDevice remoteContact) {
|
||||
super(omemoManager, omemoStore, remoteContact);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionCipher createCipher(OmemoDevice contact) {
|
||||
SignalOmemoStoreConnector connector = new SignalOmemoStoreConnector(omemoManager, omemoStore);
|
||||
return new SessionCipher(connector, connector, connector, connector,
|
||||
omemoStore.keyUtil().omemoDeviceAsAddress(contact));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CiphertextTuple encryptMessageKey(byte[] messageKey) {
|
||||
CiphertextMessage ciphertextMessage;
|
||||
ciphertextMessage = cipher.encrypt(messageKey);
|
||||
int type = (ciphertextMessage.getType() == CiphertextMessage.PREKEY_TYPE ?
|
||||
OmemoElement.TYPE_OMEMO_PREKEY_MESSAGE : OmemoElement.TYPE_OMEMO_MESSAGE);
|
||||
return new CiphertextTuple(ciphertextMessage.serialize(), type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decryptMessageKey(byte[] encryptedKey) throws NoRawSessionException {
|
||||
byte[] decryptedKey = null;
|
||||
try {
|
||||
try {
|
||||
PreKeySignalMessage message = new PreKeySignalMessage(encryptedKey);
|
||||
if (!message.getPreKeyId().isPresent()) {
|
||||
LOGGER.log(Level.WARNING, "PreKeySignalMessage did not contain a PreKeyId");
|
||||
return null;
|
||||
}
|
||||
LOGGER.log(Level.INFO, "PreKeySignalMessage received, new session ID: " + message.getSignedPreKeyId() + "/" + message.getPreKeyId().get());
|
||||
IdentityKey messageIdentityKey = message.getIdentityKey();
|
||||
if (this.identityKey != null && !this.identityKey.equals(messageIdentityKey)) {
|
||||
LOGGER.log(Level.INFO, "Had session with fingerprint " + getFingerprint() +
|
||||
", received message with different fingerprint " + omemoStore.keyUtil().getFingerprint(messageIdentityKey) +
|
||||
". Silently drop the message.");
|
||||
} else {
|
||||
this.identityKey = messageIdentityKey;
|
||||
decryptedKey = cipher.decrypt(message);
|
||||
this.preKeyId = message.getPreKeyId().get();
|
||||
}
|
||||
} catch (InvalidMessageException | InvalidVersionException e) {
|
||||
SignalMessage message = new SignalMessage(encryptedKey);
|
||||
decryptedKey = cipher.decrypt(message);
|
||||
} catch (InvalidKeyIdException e) {
|
||||
throw new NoRawSessionException(e);
|
||||
}
|
||||
catch (InvalidKeyException | UntrustedIdentityException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error decrypting message header, " + e.getClass().getName() + ": " + e.getMessage());
|
||||
}
|
||||
} catch (InvalidMessageException | NoSessionException e) {
|
||||
throw new NoRawSessionException(e);
|
||||
} catch (LegacyMessageException | DuplicateMessageException e) {
|
||||
LOGGER.log(Level.SEVERE, "Error decrypting message header, " + e.getClass().getName() + ": " + e.getMessage());
|
||||
}
|
||||
return decryptedKey;
|
||||
}
|
||||
}
|
|
@ -40,12 +40,13 @@ import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
|||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public abstract class SignalOmemoStore
|
||||
extends OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
extends OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
private final SignalOmemoKeyUtil signalKeyUtil = new SignalOmemoKeyUtil();
|
||||
|
||||
@Override
|
||||
public OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> keyUtil() {
|
||||
public OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, ECPublicKey, PreKeyBundle> keyUtil() {
|
||||
return signalKeyUtil;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,15 +21,17 @@
|
|||
package org.jivesoftware.smackx.omemo.signal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
|
@ -57,21 +59,28 @@ public class SignalOmemoStoreConnector
|
|||
|
||||
private static final Logger LOGGER = Logger.getLogger(SignalOmemoStoreConnector.class.getName());
|
||||
|
||||
private final OmemoManager omemoManager;
|
||||
private final OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
private final OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord,
|
||||
SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
omemoStore;
|
||||
private final OmemoManager omemoManager;
|
||||
|
||||
public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> store) {
|
||||
public SignalOmemoStoreConnector(OmemoManager omemoManager, OmemoStore<IdentityKeyPair,
|
||||
IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey,
|
||||
PreKeyBundle, SessionCipher> store) {
|
||||
this.omemoManager = omemoManager;
|
||||
this.omemoStore = store;
|
||||
}
|
||||
|
||||
OmemoDevice getOurDevice() {
|
||||
return omemoManager.getOwnDevice();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IdentityKeyPair getIdentityKeyPair() {
|
||||
try {
|
||||
return omemoStore.loadOmemoIdentityKeyPair(omemoManager);
|
||||
return omemoStore.loadOmemoIdentityKeyPair(getOurDevice());
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.SEVERE, "getIdentityKeyPair has failed: " + e, e);
|
||||
LOGGER.log(Level.SEVERE, "IdentityKeyPair seems to be invalid.", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -86,33 +95,41 @@ public class SignalOmemoStoreConnector
|
|||
}
|
||||
|
||||
@Override
|
||||
public void saveIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
|
||||
public boolean saveIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
|
||||
OmemoDevice device;
|
||||
try {
|
||||
omemoStore.storeOmemoIdentityKey(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress), identityKey);
|
||||
device = asOmemoDevice(signalProtocolAddress);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
omemoStore.storeOmemoIdentityKey(getOurDevice(), device, identityKey);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress signalProtocolAddress, IdentityKey identityKey) {
|
||||
// Disable internal trust management. Instead we use OmemoStore.isTrustedOmemoIdentity() before encrypting for a
|
||||
// recipient.
|
||||
public boolean isTrustedIdentity(SignalProtocolAddress signalProtocolAddress,
|
||||
IdentityKey identityKey,
|
||||
Direction direction) {
|
||||
// Disable internal trust management. Instead we use OmemoStore.isTrustedOmemoIdentity() before encrypting
|
||||
// for a recipient.
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PreKeyRecord loadPreKey(int i) throws InvalidKeyIdException {
|
||||
PreKeyRecord pr = omemoStore.loadOmemoPreKey(omemoManager, i);
|
||||
if (pr == null) {
|
||||
throw new InvalidKeyIdException("No PreKey with Id " + i + " found!");
|
||||
PreKeyRecord preKey = omemoStore.loadOmemoPreKey(getOurDevice(), i);
|
||||
|
||||
if (preKey == null) {
|
||||
throw new InvalidKeyIdException("No PreKey with Id " + i + " found.");
|
||||
}
|
||||
return pr;
|
||||
|
||||
return preKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storePreKey(int i, PreKeyRecord preKeyRecord) {
|
||||
omemoStore.storeOmemoPreKey(omemoManager, i, preKeyRecord);
|
||||
omemoStore.storeOmemoPreKey(getOurDevice(), i, preKeyRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,96 +137,113 @@ public class SignalOmemoStoreConnector
|
|||
try {
|
||||
return (loadPreKey(i) != null);
|
||||
} catch (InvalidKeyIdException e) {
|
||||
LOGGER.log(Level.WARNING, "containsPreKey has failed: " + e.getMessage());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePreKey(int i) {
|
||||
omemoStore.removeOmemoPreKey(omemoManager, i);
|
||||
omemoStore.removeOmemoPreKey(getOurDevice(), i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SessionRecord loadSession(SignalProtocolAddress signalProtocolAddress) {
|
||||
OmemoDevice device;
|
||||
try {
|
||||
SessionRecord s = omemoStore.loadRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
|
||||
return (s != null ? s : new SessionRecord());
|
||||
device = asOmemoDevice(signalProtocolAddress);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
SessionRecord record = omemoStore.loadRawSession(getOurDevice(), device);
|
||||
|
||||
if (record != null) {
|
||||
return record;
|
||||
} else {
|
||||
return new SessionRecord();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Integer> getSubDeviceSessions(String s) {
|
||||
HashMap<Integer, SessionRecord> contactsSessions;
|
||||
BareJid jid;
|
||||
try {
|
||||
contactsSessions = omemoStore.loadAllRawSessionsOf(omemoManager, JidCreate.bareFrom(s));
|
||||
jid = JidCreate.bareFrom(s);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
if (contactsSessions != null) {
|
||||
return new ArrayList<>(contactsSessions.keySet());
|
||||
}
|
||||
return new ArrayList<>();
|
||||
|
||||
return new ArrayList<>(omemoStore.loadAllRawSessionsOf(getOurDevice(), jid).keySet());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSession(SignalProtocolAddress signalProtocolAddress, SessionRecord sessionRecord) {
|
||||
OmemoDevice device;
|
||||
try {
|
||||
omemoStore.storeRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress), sessionRecord);
|
||||
device = asOmemoDevice(signalProtocolAddress);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
omemoStore.storeRawSession(getOurDevice(), device, sessionRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsSession(SignalProtocolAddress signalProtocolAddress) {
|
||||
OmemoDevice device;
|
||||
try {
|
||||
return omemoStore.containsRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
|
||||
device = asOmemoDevice(signalProtocolAddress);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
return omemoStore.containsRawSession(getOurDevice(), device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteSession(SignalProtocolAddress signalProtocolAddress) {
|
||||
OmemoDevice device;
|
||||
try {
|
||||
omemoStore.removeRawSession(omemoManager, omemoStore.keyUtil().addressAsOmemoDevice(signalProtocolAddress));
|
||||
device = asOmemoDevice(signalProtocolAddress);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
omemoStore.removeRawSession(getOurDevice(), device);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void deleteAllSessions(String s) {
|
||||
BareJid jid;
|
||||
try {
|
||||
omemoStore.removeAllRawSessionsOf(omemoManager, JidCreate.bareFrom(s));
|
||||
jid = JidCreate.bareFrom(s);
|
||||
} catch (XmppStringprepException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
|
||||
omemoStore.removeAllRawSessionsOf(getOurDevice(), jid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SignedPreKeyRecord loadSignedPreKey(int i) throws InvalidKeyIdException {
|
||||
SignedPreKeyRecord spkr = omemoStore.loadOmemoSignedPreKey(omemoManager, i);
|
||||
if (spkr == null) {
|
||||
throw new InvalidKeyIdException("No SignedPreKey with Id " + i + " found!");
|
||||
SignedPreKeyRecord signedPreKeyRecord = omemoStore.loadOmemoSignedPreKey(getOurDevice(), i);
|
||||
if (signedPreKeyRecord == null) {
|
||||
throw new InvalidKeyIdException("No signed preKey with id " + i + " found.");
|
||||
}
|
||||
return spkr;
|
||||
return signedPreKeyRecord;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SignedPreKeyRecord> loadSignedPreKeys() {
|
||||
HashMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap = omemoStore.loadOmemoSignedPreKeys(omemoManager);
|
||||
List<SignedPreKeyRecord> signedPreKeyRecordList = new ArrayList<>();
|
||||
signedPreKeyRecordList.addAll(signedPreKeyRecordHashMap.values());
|
||||
return signedPreKeyRecordList;
|
||||
|
||||
TreeMap<Integer, SignedPreKeyRecord> signedPreKeyRecordHashMap =
|
||||
omemoStore.loadOmemoSignedPreKeys(getOurDevice());
|
||||
return new ArrayList<>(signedPreKeyRecordHashMap.values());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeSignedPreKey(int i, SignedPreKeyRecord signedPreKeyRecord) {
|
||||
omemoStore.storeOmemoSignedPreKey(omemoManager, i, signedPreKeyRecord);
|
||||
omemoStore.storeOmemoSignedPreKey(getOurDevice(), i, signedPreKeyRecord);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -224,6 +258,14 @@ public class SignalOmemoStoreConnector
|
|||
|
||||
@Override
|
||||
public void removeSignedPreKey(int i) {
|
||||
omemoStore.removeOmemoSignedPreKey(omemoManager, i);
|
||||
omemoStore.removeOmemoSignedPreKey(getOurDevice(), i);
|
||||
}
|
||||
|
||||
private static OmemoDevice asOmemoDevice(SignalProtocolAddress address) throws XmppStringprepException {
|
||||
return new OmemoDevice(JidCreate.bareFrom(address.getName()), address.getDeviceId());
|
||||
}
|
||||
|
||||
public static SignalProtocolAddress asAddress(OmemoDevice device) {
|
||||
return new SignalProtocolAddress(device.getJid().toString(), device.getDeviceId());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
|
||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.CIPHERMODE;
|
||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.KEYTYPE;
|
||||
import static org.jivesoftware.smackx.omemo.util.OmemoConstants.Crypto.PROVIDER;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.InvalidKeyException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.NoSuchProviderException;
|
||||
import java.security.Security;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
import javax.crypto.NoSuchPaddingException;
|
||||
import javax.crypto.SecretKey;
|
||||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
|
||||
|
||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* Test the OmemoMessageBuilder.
|
||||
*/
|
||||
public class OmemoMessageBuilderTest extends SmackTestSuite {
|
||||
|
||||
@Test
|
||||
public void setTextTest() throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidAlgorithmParameterException, UnsupportedEncodingException, IllegalBlockSizeException, BadPaddingException, NoSuchProviderException, InvalidKeyException {
|
||||
Security.addProvider(new BouncyCastleProvider());
|
||||
String message = "Hello World!";
|
||||
byte[] key = OmemoMessageBuilder.generateKey();
|
||||
byte[] iv = OmemoMessageBuilder.generateIv();
|
||||
|
||||
SecretKey secretKey = new SecretKeySpec(key, KEYTYPE);
|
||||
IvParameterSpec ivSpec = new IvParameterSpec(iv);
|
||||
Cipher cipher = Cipher.getInstance(CIPHERMODE, PROVIDER);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secretKey, ivSpec);
|
||||
|
||||
OmemoMessageBuilder<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher>
|
||||
mb = new OmemoMessageBuilder<>(null, null, key, iv);
|
||||
mb.setMessage(message);
|
||||
|
||||
byte[] expected = cipher.doFinal(message.getBytes(StringUtils.UTF8));
|
||||
byte[] messageKey = new byte[16];
|
||||
System.arraycopy(mb.getMessageKey(),0, messageKey, 0, 16);
|
||||
byte[] messagePlusTag = new byte[mb.getCiphertextMessage().length + 16];
|
||||
System.arraycopy(mb.getCiphertextMessage(),0,messagePlusTag,0,mb.getCiphertextMessage().length);
|
||||
System.arraycopy(mb.getMessageKey(), 16, messagePlusTag, mb.getCiphertextMessage().length, 16);
|
||||
|
||||
assertArrayEquals(key, messageKey);
|
||||
assertArrayEquals(expected, messagePlusTag);
|
||||
}
|
||||
}
|
|
@ -1,218 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.powermock.api.mockito.PowerMockito.when;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Date;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.FileBasedOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.OmemoConfiguration;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.powermock.api.mockito.PowerMockito;
|
||||
import org.powermock.core.classloader.annotations.PrepareForTest;
|
||||
import org.powermock.modules.junit4.PowerMockRunner;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* Test the file-based signalOmemoStore.
|
||||
*/
|
||||
@RunWith(PowerMockRunner.class)
|
||||
@PrepareForTest({OmemoManager.class})
|
||||
public class SignalFileBasedOmemoStoreTest {
|
||||
|
||||
private static File storePath;
|
||||
private static SignalFileBasedOmemoStore omemoStore;
|
||||
private static OmemoManager omemoManager;
|
||||
|
||||
|
||||
private void deleteStore() {
|
||||
FileBasedOmemoStore.deleteDirectory(storePath);
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void setup() throws XmppStringprepException {
|
||||
String userHome = System.getProperty("user.home");
|
||||
if (userHome != null) {
|
||||
File f = new File(userHome);
|
||||
storePath = new File(f, ".config/smack-integration-test/store");
|
||||
} else {
|
||||
storePath = new File("int_test_omemo_store");
|
||||
}
|
||||
|
||||
OmemoConfiguration.setFileBasedOmemoStoreDefaultPath(storePath);
|
||||
omemoStore = new SignalFileBasedOmemoStore();
|
||||
|
||||
OmemoDevice device = new OmemoDevice(JidCreate.bareFrom("storeTest@server.tld"), 55155);
|
||||
omemoManager = PowerMockito.mock(OmemoManager.class);
|
||||
when(omemoManager.getDeviceId()).thenReturn(device.getDeviceId());
|
||||
when(omemoManager.getOwnJid()).thenReturn(device.getJid());
|
||||
when(omemoManager.getOwnDevice()).thenReturn(device);
|
||||
}
|
||||
|
||||
@Before
|
||||
public void before() {
|
||||
deleteStore();
|
||||
}
|
||||
|
||||
@After
|
||||
public void after() {
|
||||
deleteStore();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isFreshInstallationTest() {
|
||||
assertTrue(omemoStore.isFreshInstallation(omemoManager));
|
||||
omemoStore.storeOmemoIdentityKeyPair(omemoManager, omemoStore.generateOmemoIdentityKeyPair());
|
||||
assertFalse(omemoStore.isFreshInstallation(omemoManager));
|
||||
omemoStore.purgeOwnDeviceKeys(omemoManager);
|
||||
assertTrue(omemoStore.isFreshInstallation(omemoManager));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void defaultDeviceIdTest() throws XmppStringprepException {
|
||||
assertEquals(-1, omemoStore.getDefaultDeviceId(omemoManager.getOwnJid()));
|
||||
omemoStore.setDefaultDeviceId(omemoManager.getOwnJid(), 55);
|
||||
assertEquals(55, omemoStore.getDefaultDeviceId(omemoManager.getOwnJid()));
|
||||
assertEquals(-1, omemoStore.getDefaultDeviceId(JidCreate.bareFrom("randomGuy@server.tld")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cachedDeviceListTest() throws XmppStringprepException {
|
||||
OmemoDevice bob = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 666);
|
||||
OmemoDevice craig = new OmemoDevice(JidCreate.bareFrom("craig@southpark.tv"), 3333333);
|
||||
|
||||
CachedDeviceList bobsList = new CachedDeviceList();
|
||||
assertEquals(0, bobsList.getAllDevices().size());
|
||||
bobsList.getActiveDevices().add(bob.getDeviceId());
|
||||
bobsList.getActiveDevices().add(777);
|
||||
bobsList.getInactiveDevices().add(888);
|
||||
|
||||
CachedDeviceList craigsList = new CachedDeviceList();
|
||||
craigsList.addDevice(craig.getDeviceId());
|
||||
|
||||
assertEquals(3, bobsList.getAllDevices().size());
|
||||
assertEquals(2, bobsList.getActiveDevices().size());
|
||||
assertTrue(bobsList.getInactiveDevices().contains(888));
|
||||
assertTrue(bobsList.getActiveDevices().contains(777));
|
||||
assertTrue(bobsList.getAllDevices().contains(888));
|
||||
|
||||
assertEquals(0, craigsList.getInactiveDevices().size());
|
||||
assertEquals(1, craigsList.getActiveDevices().size());
|
||||
assertEquals(1, craigsList.getAllDevices().size());
|
||||
assertEquals(craig.getDeviceId(), craigsList.getActiveDevices().iterator().next().intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void omemoIdentityKeyPairTest() throws CorruptedOmemoKeyException {
|
||||
assertNull(omemoStore.loadOmemoIdentityKeyPair(omemoManager));
|
||||
omemoStore.storeOmemoIdentityKeyPair(omemoManager, omemoStore.generateOmemoIdentityKeyPair());
|
||||
IdentityKeyPair ikp = omemoStore.loadOmemoIdentityKeyPair(omemoManager);
|
||||
assertNotNull(ikp);
|
||||
|
||||
assertTrue(omemoStore.keyUtil().getFingerprint(ikp.getPublicKey()).equals(omemoStore.getFingerprint(omemoManager)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signedPreKeyTest() throws CorruptedOmemoKeyException {
|
||||
assertEquals(0, omemoStore.loadOmemoSignedPreKeys(omemoManager).size());
|
||||
IdentityKeyPair ikp = omemoStore.generateOmemoIdentityKeyPair();
|
||||
SignedPreKeyRecord spk = omemoStore.generateOmemoSignedPreKey(ikp, 14);
|
||||
omemoStore.storeOmemoSignedPreKey(omemoManager, 14, spk);
|
||||
assertEquals(1, omemoStore.loadOmemoSignedPreKeys(omemoManager).size());
|
||||
assertNotNull(omemoStore.loadOmemoSignedPreKey(omemoManager, 14));
|
||||
assertArrayEquals(spk.serialize(), omemoStore.loadOmemoSignedPreKey(omemoManager, 14).serialize());
|
||||
assertNull(omemoStore.loadOmemoSignedPreKey(omemoManager, 13));
|
||||
assertEquals(0, omemoStore.loadCurrentSignedPreKeyId(omemoManager));
|
||||
omemoStore.storeCurrentSignedPreKeyId(omemoManager, 15);
|
||||
assertEquals(15, omemoStore.loadCurrentSignedPreKeyId(omemoManager));
|
||||
omemoStore.removeOmemoSignedPreKey(omemoManager, 14);
|
||||
assertNull(omemoStore.loadOmemoSignedPreKey(omemoManager, 14));
|
||||
|
||||
assertNull(omemoStore.getDateOfLastSignedPreKeyRenewal(omemoManager));
|
||||
Date now = new Date();
|
||||
omemoStore.setDateOfLastSignedPreKeyRenewal(omemoManager, now);
|
||||
assertEquals(now, omemoStore.getDateOfLastSignedPreKeyRenewal(omemoManager));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preKeyTest() {
|
||||
assertEquals(0, omemoStore.loadOmemoPreKeys(omemoManager).size());
|
||||
assertNull(omemoStore.loadOmemoPreKey(omemoManager, 12));
|
||||
omemoStore.storeOmemoPreKeys(omemoManager,
|
||||
omemoStore.generateOmemoPreKeys(1, 20));
|
||||
assertNotNull(omemoStore.loadOmemoPreKey(omemoManager, 12));
|
||||
assertEquals(20, omemoStore.loadOmemoPreKeys(omemoManager).size());
|
||||
omemoStore.removeOmemoPreKey(omemoManager, 12);
|
||||
assertNull(omemoStore.loadOmemoPreKey(omemoManager, 12));
|
||||
assertEquals(19, omemoStore.loadOmemoPreKeys(omemoManager).size());
|
||||
|
||||
assertEquals(0, omemoStore.loadLastPreKeyId(omemoManager));
|
||||
omemoStore.storeLastPreKeyId(omemoManager, 35);
|
||||
assertEquals(35, omemoStore.loadLastPreKeyId(omemoManager));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void trustingTest() throws XmppStringprepException, CorruptedOmemoKeyException {
|
||||
OmemoDevice bob = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 555);
|
||||
IdentityKey bobsKey = omemoStore.generateOmemoIdentityKeyPair().getPublicKey();
|
||||
assertFalse(omemoStore.isDecidedOmemoIdentity(omemoManager, bob, bobsKey));
|
||||
assertFalse(omemoStore.isTrustedOmemoIdentity(omemoManager, bob, bobsKey));
|
||||
omemoStore.trustOmemoIdentity(omemoManager, bob, bobsKey);
|
||||
assertTrue(omemoStore.isDecidedOmemoIdentity(omemoManager, bob, omemoStore.keyUtil().getFingerprint(bobsKey)));
|
||||
assertTrue(omemoStore.isTrustedOmemoIdentity(omemoManager, bob, omemoStore.keyUtil().getFingerprint(bobsKey)));
|
||||
assertNull(omemoStore.loadOmemoIdentityKey(omemoManager, bob));
|
||||
omemoStore.storeOmemoIdentityKey(omemoManager, bob, bobsKey);
|
||||
assertNotNull(omemoStore.loadOmemoIdentityKey(omemoManager, bob));
|
||||
IdentityKey bobsOtherKey = omemoStore.generateOmemoIdentityKeyPair().getPublicKey();
|
||||
assertFalse(omemoStore.isTrustedOmemoIdentity(omemoManager, bob, bobsOtherKey));
|
||||
assertFalse(omemoStore.isDecidedOmemoIdentity(omemoManager, bob, bobsOtherKey));
|
||||
omemoStore.distrustOmemoIdentity(omemoManager, bob, omemoStore.keyUtil().getFingerprint(bobsKey));
|
||||
assertTrue(omemoStore.isDecidedOmemoIdentity(omemoManager, bob, bobsKey));
|
||||
assertFalse(omemoStore.isTrustedOmemoIdentity(omemoManager, bob, bobsKey));
|
||||
|
||||
assertNull(omemoStore.getDateOfLastReceivedMessage(omemoManager, bob));
|
||||
Date now = new Date();
|
||||
omemoStore.setDateOfLastReceivedMessage(omemoManager, bob, now);
|
||||
assertEquals(now, omemoStore.getDateOfLastReceivedMessage(omemoManager, bob));
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNotSame;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
import static junit.framework.TestCase.fail;
|
||||
|
||||
import org.jivesoftware.smack.test.util.SmackTestSuite;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* Test SignalOmemoKeyUtil methods.
|
||||
*
|
||||
* @author Paul Schaub
|
||||
*/
|
||||
public class LegacySignalOmemoKeyUtilTest extends SmackTestSuite {
|
||||
|
||||
private final SignalOmemoKeyUtil keyUtil = new SignalOmemoKeyUtil();
|
||||
|
||||
@Test
|
||||
public void omemoIdentityKeyPairSerializationTest() throws CorruptedOmemoKeyException {
|
||||
IdentityKeyPair ikp = keyUtil.generateOmemoIdentityKeyPair();
|
||||
byte[] bytes = keyUtil.identityKeyPairToBytes(ikp);
|
||||
assertNotNull("serialized identityKeyPair must not be null.",
|
||||
bytes);
|
||||
assertNotSame("serialized identityKeyPair must not be of length 0.",
|
||||
0, bytes.length);
|
||||
|
||||
IdentityKeyPair ikp2 = keyUtil.identityKeyPairFromBytes(bytes);
|
||||
assertTrue("Deserialized IdentityKeyPairs PublicKey must equal the originals one.",
|
||||
ikp.getPublicKey().equals(ikp2.getPublicKey()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void omemoIdentityKeySerializationTest() {
|
||||
IdentityKey k = keyUtil.generateOmemoIdentityKeyPair().getPublicKey();
|
||||
|
||||
try {
|
||||
assertEquals("Deserialized IdentityKey must equal the original one.",
|
||||
k, keyUtil.identityKeyFromBytes(keyUtil.identityKeyToBytes(k)));
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
fail("Caught exception while serializing and deserializing identityKey (" + e + "): " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateOmemoSignedPreKeyTest() {
|
||||
IdentityKeyPair ikp = keyUtil.generateOmemoIdentityKeyPair();
|
||||
try {
|
||||
SignedPreKeyRecord spk = keyUtil.generateOmemoSignedPreKey(ikp, 1);
|
||||
assertNotNull("SignedPreKey must not be null.", spk);
|
||||
assertEquals("SignedPreKeyId must match.", 1, spk.getId());
|
||||
assertEquals("singedPreKeyId must match here also.", 1, keyUtil.signedPreKeyIdFromKey(spk));
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
fail("Caught an exception while generating signedPreKey (" + e + "): " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFingerprintTest() {
|
||||
IdentityKeyPair ikp = keyUtil.generateOmemoIdentityKeyPair();
|
||||
IdentityKey ik = ikp.getPublicKey();
|
||||
assertTrue("Length of fingerprint must be 64.",
|
||||
keyUtil.getFingerprintOfIdentityKey(ik).length() == 64);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
||||
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* smack-omemo-signal implementation of {@link OmemoKeyUtilTest}.
|
||||
* This class executes tests of its super class with available implementations of {@link OmemoKeyUtil}.
|
||||
* So far this includes {@link SignalOmemoKeyUtil}.
|
||||
*/
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class SignalOmemoKeyUtilTest
|
||||
extends OmemoKeyUtilTest<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, ECPublicKey, PreKeyBundle> {
|
||||
|
||||
public SignalOmemoKeyUtilTest(OmemoKeyUtil<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, ECPublicKey, PreKeyBundle> keyUtil) {
|
||||
super(keyUtil);
|
||||
}
|
||||
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> getParameters() {
|
||||
return Arrays.asList(new Object[][] {
|
||||
{ new SignalOmemoKeyUtil()}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@
|
|||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
|
@ -26,26 +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.OmemoManager;
|
||||
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,10 +39,10 @@ import org.junit.Test;
|
|||
/**
|
||||
* Test OmemoManager functionality.
|
||||
*/
|
||||
public class OmemoManagerTest extends SmackTestSuite {
|
||||
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();
|
||||
|
||||
|
@ -73,8 +58,8 @@ public class OmemoManagerTest extends SmackTestSuite {
|
|||
assertNotNull(c);
|
||||
assertNotNull(d);
|
||||
|
||||
assertEquals(123, a.getDeviceId());
|
||||
assertEquals(234, b.getDeviceId());
|
||||
assertEquals(Integer.valueOf(123), a.getDeviceId());
|
||||
assertEquals(Integer.valueOf(234), b.getDeviceId());
|
||||
|
||||
assertFalse(a == b);
|
||||
assertFalse(a == c);
|
|
@ -18,7 +18,7 @@
|
|||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
@ -41,6 +41,6 @@ public class SignalOmemoStoreConnectorTest {
|
|||
@Test
|
||||
public void isTrustedIdentityTest() {
|
||||
SignalOmemoStoreConnector connector = new SignalOmemoStoreConnector(null, null);
|
||||
assertTrue("All identities must be trusted by default.", connector.isTrustedIdentity(null, null));
|
||||
assertTrue("All identities must be trusted by default.", connector.isTrustedIdentity(null, null, null));
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* This file is part of smack-omemo-signal.
|
||||
*
|
||||
* smack-omemo-signal is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software Foundation,
|
||||
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalCachingOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalFileBasedOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.signal.SignalOmemoKeyUtil;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Parameterized;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
import org.whispersystems.libsignal.IdentityKey;
|
||||
import org.whispersystems.libsignal.IdentityKeyPair;
|
||||
import org.whispersystems.libsignal.SessionCipher;
|
||||
import org.whispersystems.libsignal.SignalProtocolAddress;
|
||||
import org.whispersystems.libsignal.ecc.ECPublicKey;
|
||||
import org.whispersystems.libsignal.state.PreKeyBundle;
|
||||
import org.whispersystems.libsignal.state.PreKeyRecord;
|
||||
import org.whispersystems.libsignal.state.SessionRecord;
|
||||
import org.whispersystems.libsignal.state.SignedPreKeyRecord;
|
||||
|
||||
/**
|
||||
* smack-omemo-signal implementation of {@link OmemoStoreTest}.
|
||||
* This class executes tests of its super class with available implementations of {@link OmemoStore}.
|
||||
* So far this includes {@link SignalFileBasedOmemoStore}, {@link SignalCachingOmemoStore}.
|
||||
*/
|
||||
@RunWith(value = Parameterized.class)
|
||||
public class SignalOmemoStoreTest extends OmemoStoreTest<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> {
|
||||
|
||||
public SignalOmemoStoreTest(OmemoStore<IdentityKeyPair, IdentityKey, PreKeyRecord, SignedPreKeyRecord, SessionRecord, SignalProtocolAddress, ECPublicKey, PreKeyBundle, SessionCipher> store)
|
||||
throws XmppStringprepException {
|
||||
super(store);
|
||||
}
|
||||
|
||||
/**
|
||||
* We are running this Test with multiple available OmemoStore implementations.
|
||||
* @return
|
||||
* @throws IOException
|
||||
*/
|
||||
@Parameterized.Parameters
|
||||
public static Collection<Object[]> getParameters() throws IOException {
|
||||
TemporaryFolder temp = initStaticTemp();
|
||||
return Arrays.asList(new Object[][] {
|
||||
// Simple file based store
|
||||
{ new SignalFileBasedOmemoStore(temp.newFolder("sigFileBased"))},
|
||||
// Ephemeral caching store
|
||||
{ new SignalCachingOmemoStore()},
|
||||
// Caching file based store
|
||||
{ new SignalCachingOmemoStore(new SignalFileBasedOmemoStore(temp.newFolder("cachingSigFileBased")))}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void keyUtilTest() {
|
||||
assertTrue(store.keyUtil() instanceof SignalOmemoKeyUtil);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue