mirror of
https://github.com/vanitasvitae/Smack.git
synced 2025-09-12 02:29:38 +02:00
Move trust management from OmemoStore to TrustCallback
Refactor various method names to more precisely reflect what happens Introduce CachingOmemoStore, SignalCachingOmemoStore, which can be either used standalone as ephemeral OmemoStore implementations, or as wrappers around other implementations to add a cache layer for reduced storage access. Get rid of "isFreshInstallation". Keys are now - given that they don't exist - generated on startup. Bump libsignal-protocol-java to 2.6.2 Prevent offline access to some functions which require authenticated connection Create more advanced unit tests and integration tests Add async initialization function for OmemoManager Remove session handling from smack-omemo. This is now handled - in case of smack-omemo-signal - solely by libsignal-protocol-java
This commit is contained in:
parent
fb7a22a761
commit
36dae1ece4
53 changed files with 4072 additions and 3408 deletions
|
@ -0,0 +1,423 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.TreeSet;
|
||||
|
||||
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.util.OmemoKeyUtil;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
||||
/**
|
||||
* This class implements the Proxy Pattern in order to wrap an OmemoStore with a caching layer.
|
||||
* This reduces access to the underlying storage layer (eg. database, filesystem) by only accessing it for
|
||||
* missing/updated values.
|
||||
*
|
||||
* Alternatively this implementation can be used as an ephemeral keystore without a persisting backend.
|
||||
*
|
||||
* @param <T_IdKeyPair>
|
||||
* @param <T_IdKey>
|
||||
* @param <T_PreKey>
|
||||
* @param <T_SigPreKey>
|
||||
* @param <T_Sess>
|
||||
* @param <T_Addr>
|
||||
* @param <T_ECPub>
|
||||
* @param <T_Bundle>
|
||||
* @param <T_Ciph>
|
||||
*/
|
||||
public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
extends OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
{
|
||||
|
||||
private final HashMap<OmemoDevice, KeyCache<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess>> caches = new HashMap<>();
|
||||
private final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> persistent;
|
||||
private final OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> keyUtil;
|
||||
|
||||
public CachingOmemoStore(OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> keyUtil) {
|
||||
if (keyUtil == null) {
|
||||
throw new IllegalArgumentException("KeyUtil MUST NOT be null!");
|
||||
}
|
||||
this.keyUtil = keyUtil;
|
||||
persistent = null;
|
||||
}
|
||||
|
||||
public CachingOmemoStore(OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> wrappedStore) {
|
||||
this.keyUtil = null;
|
||||
persistent = wrappedStore;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SortedSet<Integer> localDeviceIdsOf(BareJid localUser) {
|
||||
if (persistent != null) {
|
||||
return persistent.localDeviceIdsOf(localUser);
|
||||
} else {
|
||||
return new TreeSet<>(); //TODO: ?
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T_IdKeyPair loadOmemoIdentityKeyPair(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKeyPair pair = getCache(userDevice).identityKeyPair;
|
||||
|
||||
if (pair == null && persistent != null) {
|
||||
pair = persistent.loadOmemoIdentityKeyPair(userDevice);
|
||||
if (pair != null) {
|
||||
getCache(userDevice).identityKeyPair = pair;
|
||||
}
|
||||
}
|
||||
|
||||
return pair;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeOmemoIdentityKeyPair(OmemoDevice userDevice, T_IdKeyPair identityKeyPair) {
|
||||
getCache(userDevice).identityKeyPair = identityKeyPair;
|
||||
if (persistent != null) {
|
||||
persistent.storeOmemoIdentityKeyPair(userDevice, identityKeyPair);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOmemoIdentityKeyPair(OmemoDevice userDevice) {
|
||||
getCache(userDevice).identityKeyPair = null;
|
||||
if (persistent != null) {
|
||||
persistent.removeOmemoIdentityKeyPair(userDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T_IdKey loadOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKey idKey = getCache(userDevice).identityKeys.get(contactsDevice);
|
||||
|
||||
if (idKey == null && persistent != null) {
|
||||
idKey = persistent.loadOmemoIdentityKey(userDevice, contactsDevice);
|
||||
if (idKey != null) {
|
||||
getCache(userDevice).identityKeys.put(contactsDevice, idKey);
|
||||
}
|
||||
}
|
||||
|
||||
return idKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice device, T_IdKey t_idKey) {
|
||||
getCache(userDevice).identityKeys.put(device, t_idKey);
|
||||
if (persistent != null) {
|
||||
persistent.storeOmemoIdentityKey(userDevice, device, t_idKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
getCache(userDevice).identityKeys.remove(contactsDevice);
|
||||
if (persistent != null) {
|
||||
persistent.removeOmemoIdentityKey(userDevice, contactsDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice from, Date date) {
|
||||
getCache(userDevice).lastMessagesDates.put(from, date);
|
||||
if (persistent != null) {
|
||||
persistent.setDateOfLastReceivedMessage(userDevice, from, date);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice from) {
|
||||
Date last = getCache(userDevice).lastMessagesDates.get(from);
|
||||
|
||||
if (last == null && persistent != null) {
|
||||
last = persistent.getDateOfLastReceivedMessage(userDevice, from);
|
||||
if (last != null) {
|
||||
getCache(userDevice).lastMessagesDates.put(from, last);
|
||||
}
|
||||
}
|
||||
|
||||
return last;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date) {
|
||||
getCache(userDevice).lastRenewalDate = date;
|
||||
if (persistent != null) {
|
||||
persistent.setDateOfLastSignedPreKeyRenewal(userDevice, date);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Date getDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice) {
|
||||
Date lastRenewal = getCache(userDevice).lastRenewalDate;
|
||||
|
||||
if (lastRenewal == null && persistent != null) {
|
||||
lastRenewal = persistent.getDateOfLastSignedPreKeyRenewal(userDevice);
|
||||
if (lastRenewal != null) {
|
||||
getCache(userDevice).lastRenewalDate = lastRenewal;
|
||||
}
|
||||
}
|
||||
|
||||
return lastRenewal;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T_PreKey loadOmemoPreKey(OmemoDevice userDevice, int preKeyId) {
|
||||
T_PreKey preKey = getCache(userDevice).preKeys.get(preKeyId);
|
||||
|
||||
if (preKey == null && persistent != null) {
|
||||
preKey = persistent.loadOmemoPreKey(userDevice, preKeyId);
|
||||
if (preKey != null) {
|
||||
getCache(userDevice).preKeys.put(preKeyId, preKey);
|
||||
}
|
||||
}
|
||||
|
||||
return preKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeOmemoPreKey(OmemoDevice userDevice, int preKeyId, T_PreKey t_preKey) {
|
||||
getCache(userDevice).preKeys.put(preKeyId, t_preKey);
|
||||
if (persistent != null) {
|
||||
persistent.storeOmemoPreKey(userDevice, preKeyId, t_preKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOmemoPreKey(OmemoDevice userDevice, int preKeyId) {
|
||||
getCache(userDevice).preKeys.remove(preKeyId);
|
||||
if (persistent != null) {
|
||||
persistent.removeOmemoPreKey(userDevice, preKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoDevice userDevice) {
|
||||
TreeMap<Integer, T_PreKey> preKeys = getCache(userDevice).preKeys;
|
||||
|
||||
if (preKeys.isEmpty() && persistent != null) {
|
||||
preKeys.putAll(persistent.loadOmemoPreKeys(userDevice));
|
||||
}
|
||||
|
||||
return new TreeMap<>(preKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public T_SigPreKey loadOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId) {
|
||||
T_SigPreKey sigPreKey = getCache(userDevice).signedPreKeys.get(signedPreKeyId);
|
||||
|
||||
if (sigPreKey == null && persistent != null) {
|
||||
sigPreKey = persistent.loadOmemoSignedPreKey(userDevice, signedPreKeyId);
|
||||
if (sigPreKey != null) {
|
||||
getCache(userDevice).signedPreKeys.put(signedPreKeyId, sigPreKey);
|
||||
}
|
||||
}
|
||||
|
||||
return sigPreKey;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TreeMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoDevice userDevice) {
|
||||
TreeMap<Integer, T_SigPreKey> sigPreKeys = getCache(userDevice).signedPreKeys;
|
||||
|
||||
if (sigPreKeys.isEmpty() && persistent != null) {
|
||||
sigPreKeys.putAll(persistent.loadOmemoSignedPreKeys(userDevice));
|
||||
}
|
||||
|
||||
return new TreeMap<>(sigPreKeys);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeOmemoSignedPreKey(OmemoDevice userDevice,
|
||||
int signedPreKeyId,
|
||||
T_SigPreKey signedPreKey) {
|
||||
getCache(userDevice).signedPreKeys.put(signedPreKeyId, signedPreKey);
|
||||
if (persistent != null) {
|
||||
persistent.storeOmemoSignedPreKey(userDevice, signedPreKeyId, signedPreKey);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId) {
|
||||
getCache(userDevice).signedPreKeys.remove(signedPreKeyId);
|
||||
if (persistent != null) {
|
||||
persistent.removeOmemoSignedPreKey(userDevice, signedPreKeyId);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T_Sess loadRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
HashMap<Integer, T_Sess> contactSessions = getCache(userDevice).sessions.get(contactsDevice.getJid());
|
||||
if (contactSessions == null) {
|
||||
contactSessions = new HashMap<>();
|
||||
getCache(userDevice).sessions.put(contactsDevice.getJid(), contactSessions);
|
||||
}
|
||||
|
||||
T_Sess session = contactSessions.get(contactsDevice.getDeviceId());
|
||||
if (session == null && persistent != null) {
|
||||
session = persistent.loadRawSession(userDevice, contactsDevice);
|
||||
if (session != null) {
|
||||
contactSessions.put(contactsDevice.getDeviceId(), session);
|
||||
}
|
||||
}
|
||||
|
||||
return session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) {
|
||||
HashMap<Integer, T_Sess> sessions = getCache(userDevice).sessions.get(contact);
|
||||
if (sessions == null) {
|
||||
sessions = new HashMap<>();
|
||||
getCache(userDevice).sessions.put(contact, sessions);
|
||||
}
|
||||
|
||||
if (sessions.isEmpty() && persistent != null) {
|
||||
sessions.putAll(persistent.loadAllRawSessionsOf(userDevice, contact));
|
||||
}
|
||||
|
||||
return new HashMap<>(sessions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevicece, T_Sess session) {
|
||||
HashMap<Integer, T_Sess> sessions = getCache(userDevice).sessions.get(contactsDevicece.getJid());
|
||||
if (sessions == null) {
|
||||
sessions = new HashMap<>();
|
||||
getCache(userDevice).sessions.put(contactsDevicece.getJid(), sessions);
|
||||
}
|
||||
|
||||
sessions.put(contactsDevicece.getDeviceId(), session);
|
||||
if (persistent != null) {
|
||||
persistent.storeRawSession(userDevice, contactsDevicece, session);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
HashMap<Integer, T_Sess> sessions = getCache(userDevice).sessions.get(contactsDevice.getJid());
|
||||
if (sessions != null) {
|
||||
sessions.remove(contactsDevice.getDeviceId());
|
||||
}
|
||||
|
||||
if (persistent != null) {
|
||||
persistent.removeRawSession(userDevice, contactsDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAllRawSessionsOf(OmemoDevice userDevice, BareJid contact) {
|
||||
getCache(userDevice).sessions.remove(contact);
|
||||
if (persistent != null) {
|
||||
persistent.removeAllRawSessionsOf(userDevice, contact);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice) {
|
||||
HashMap<Integer, T_Sess> sessions = getCache(userDevice).sessions.get(contactsDevice.getJid());
|
||||
|
||||
return (sessions != null && sessions.get(contactsDevice.getDeviceId()) != null) ||
|
||||
(persistent != null && persistent.containsRawSession(userDevice, contactsDevice));
|
||||
}
|
||||
|
||||
@Override
|
||||
public CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact) {
|
||||
CachedDeviceList list = getCache(userDevice).deviceLists.get(contact);
|
||||
|
||||
if (list == null && persistent != null) {
|
||||
list = persistent.loadCachedDeviceList(userDevice, contact);
|
||||
if (list != null) {
|
||||
getCache(userDevice).deviceLists.put(contact, list);
|
||||
}
|
||||
}
|
||||
|
||||
return list == null ? new CachedDeviceList() : new CachedDeviceList(list);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void storeCachedDeviceList(OmemoDevice userDevice,
|
||||
BareJid contact,
|
||||
CachedDeviceList deviceList) {
|
||||
getCache(userDevice).deviceLists.put(contact, new CachedDeviceList(deviceList));
|
||||
|
||||
if (persistent != null) {
|
||||
persistent.storeCachedDeviceList(userDevice, contact, deviceList);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void purgeOwnDeviceKeys(OmemoDevice userDevice) {
|
||||
caches.remove(userDevice);
|
||||
|
||||
if (persistent != null) {
|
||||
persistent.purgeOwnDeviceKeys(userDevice);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
keyUtil() {
|
||||
if (persistent != null) {
|
||||
return persistent.keyUtil();
|
||||
} else {
|
||||
return keyUtil;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the {@link KeyCache} object of an {@link OmemoManager}.
|
||||
* @param device
|
||||
* @return
|
||||
*/
|
||||
private KeyCache<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess> getCache(OmemoDevice device) {
|
||||
KeyCache<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess> cache = caches.get(device);
|
||||
if (cache == null) {
|
||||
cache = new KeyCache<>();
|
||||
caches.put(device, cache);
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Cache that stores values for an {@link OmemoManager}.
|
||||
* @param <T_IdKeyPair>
|
||||
* @param <T_IdKey>
|
||||
* @param <T_PreKey>
|
||||
* @param <T_SigPreKey>
|
||||
* @param <T_Sess>
|
||||
*/
|
||||
private static class KeyCache<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess> {
|
||||
private T_IdKeyPair identityKeyPair;
|
||||
private final TreeMap<Integer, T_PreKey> preKeys = new TreeMap<>();
|
||||
private final TreeMap<Integer, T_SigPreKey> signedPreKeys = new TreeMap<>();
|
||||
private final HashMap<BareJid, HashMap<Integer, T_Sess>> sessions = new HashMap<>();
|
||||
private final HashMap<OmemoDevice, T_IdKey> identityKeys = new HashMap<>();
|
||||
private final HashMap<OmemoDevice, Date> lastMessagesDates = new HashMap<>();
|
||||
private final HashMap<BareJid, CachedDeviceList> deviceLists = new HashMap<>();
|
||||
private Date lastRenewalDate = null;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load diff
|
@ -39,10 +39,6 @@ public class OmemoFingerprint implements CharSequence {
|
|||
return fingerprintString.subSequence(start, end);
|
||||
}
|
||||
|
||||
public CharSequence subSequence(int start) {
|
||||
return fingerprintString.subSequence(start, fingerprintString.length() - 1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return fingerprintString;
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -0,0 +1,152 @@
|
|||
/**
|
||||
*
|
||||
* 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;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
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.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.UntrustedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
public abstract class OmemoSessionManager<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||
private static final Logger LOGGER = Logger.getLogger(OmemoSessionManager.class.getName());
|
||||
|
||||
protected final OmemoManager.KnownBareJidGuard managerGuard;
|
||||
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
||||
|
||||
public OmemoSessionManager(OmemoManager.KnownBareJidGuard managerGuard,
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store) {
|
||||
this.managerGuard = managerGuard;
|
||||
this.store = store;
|
||||
}
|
||||
|
||||
public abstract byte[] doubleRatchetDecrypt(OmemoDevice sender, byte[] encryptedKey)
|
||||
throws CorruptedOmemoKeyException, NoRawSessionException, CryptoFailedException,
|
||||
UntrustedOmemoIdentityException;
|
||||
|
||||
public abstract CiphertextTuple doubleRatchetEncrypt(OmemoDevice recipient, byte[] messageKey);
|
||||
|
||||
/**
|
||||
* Try to decrypt the transported message key using the double ratchet session.
|
||||
*
|
||||
* @param element omemoElement
|
||||
* @return tuple of cipher generated from the unpacked message key and the authtag
|
||||
* @throws CryptoFailedException if decryption using the double ratchet fails
|
||||
* @throws NoRawSessionException if we have no session, but the element was NOT a PreKeyMessage
|
||||
*/
|
||||
public CipherAndAuthTag retrieveMessageKeyAndAuthTag(OmemoDevice sender, OmemoElement element) throws CryptoFailedException,
|
||||
NoRawSessionException {
|
||||
int keyId = managerGuard.get().getDeviceId();
|
||||
byte[] unpackedKey = null;
|
||||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||
List<OmemoElement.OmemoHeader.Key> keys = element.getHeader().getKeys();
|
||||
// Find key with our ID.
|
||||
for (OmemoElement.OmemoHeader.Key k : keys) {
|
||||
if (k.getId() == keyId) {
|
||||
try {
|
||||
unpackedKey = doubleRatchetDecrypt(sender, k.getData());
|
||||
break;
|
||||
} catch (CryptoFailedException e) {
|
||||
// There might be multiple keys with our id, but we can only decrypt one.
|
||||
// So we can't throw the exception, when decrypting the first duplicate which is not for us.
|
||||
decryptExceptions.add(e);
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
decryptExceptions.add(new CryptoFailedException(e));
|
||||
} catch (UntrustedOmemoIdentityException e) {
|
||||
LOGGER.log(Level.WARNING, "Received message from " + sender + " contained unknown identityKey. Ignore message.", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unpackedKey == null) {
|
||||
if (!decryptExceptions.isEmpty()) {
|
||||
throw MultipleCryptoFailedException.from(decryptExceptions);
|
||||
}
|
||||
|
||||
throw new CryptoFailedException("Transported key could not be decrypted, since no suitable message key " +
|
||||
"was provided. Provides keys: " + keys);
|
||||
}
|
||||
|
||||
// Split in AES auth-tag and key
|
||||
byte[] messageKey = new byte[16];
|
||||
byte[] authTag = null;
|
||||
|
||||
if (unpackedKey.length == 32) {
|
||||
authTag = new byte[16];
|
||||
// copy key part into messageKey
|
||||
System.arraycopy(unpackedKey, 0, messageKey, 0, 16);
|
||||
// copy tag part into authTag
|
||||
System.arraycopy(unpackedKey, 16, authTag, 0,16);
|
||||
} else if (element.isKeyTransportElement() && unpackedKey.length == 16) {
|
||||
messageKey = unpackedKey;
|
||||
} else {
|
||||
throw new CryptoFailedException("MessageKey has wrong length: "
|
||||
+ unpackedKey.length + ". Probably legacy auth tag format.");
|
||||
}
|
||||
|
||||
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the symmetric key in cipherAndAuthTag to decrypt the payload of the omemoMessage.
|
||||
* The decrypted payload will be the body of the returned Message.
|
||||
*
|
||||
* @param element omemoElement containing a payload.
|
||||
* @param cipherAndAuthTag cipher and authentication tag.
|
||||
* @return Message containing the decrypted payload in its body.
|
||||
* @throws CryptoFailedException
|
||||
*/
|
||||
public static Message decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag) throws CryptoFailedException {
|
||||
if (!element.isMessageElement()) {
|
||||
throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
|
||||
}
|
||||
|
||||
if (cipherAndAuthTag.getAuthTag() == null || cipherAndAuthTag.getAuthTag().length != 16) {
|
||||
throw new CryptoFailedException("AuthenticationTag is null or has wrong length: "
|
||||
+ (cipherAndAuthTag.getAuthTag() == null ? "null" : cipherAndAuthTag.getAuthTag().length));
|
||||
}
|
||||
byte[] encryptedBody = new byte[element.getPayload().length + 16];
|
||||
byte[] payload = element.getPayload();
|
||||
System.arraycopy(payload, 0, encryptedBody, 0, payload.length);
|
||||
System.arraycopy(cipherAndAuthTag.getAuthTag(), 0, encryptedBody, payload.length, 16);
|
||||
|
||||
try {
|
||||
String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
|
||||
Message decrypted = new Message();
|
||||
decrypted.setBody(plaintext);
|
||||
return decrypted;
|
||||
|
||||
} catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,20 +21,17 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.TARGET_PRE_KEY_C
|
|||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smack.roster.Roster;
|
||||
import org.jivesoftware.smack.roster.RosterEntry;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoDeviceListElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CannotEstablishOmemoSessionException;
|
||||
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.internal.OmemoSession;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoKeyUtil;
|
||||
|
||||
import org.jxmpp.jid.BareJid;
|
||||
|
@ -56,9 +53,6 @@ import org.jxmpp.jid.BareJid;
|
|||
public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||
private static final Logger LOGGER = Logger.getLogger(OmemoStore.class.getName());
|
||||
|
||||
private final WeakHashMap<OmemoManager, HashMap<OmemoDevice, OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>>>
|
||||
omemoSessions = new WeakHashMap<>();
|
||||
|
||||
/**
|
||||
* Create a new OmemoStore.
|
||||
*/
|
||||
|
@ -67,26 +61,28 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
}
|
||||
|
||||
/**
|
||||
* Return true if this is a fresh installation.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @return true or false.
|
||||
* Returns a sorted set of all the deviceIds, the localUser has had data stored under in the store.
|
||||
* Basically this returns the deviceIds of all "accounts" of localUser, which are known to the store.
|
||||
* @param localUser BareJid of the user.
|
||||
* @return set of deviceIds with available data.
|
||||
*/
|
||||
public abstract boolean isFreshInstallation(OmemoManager omemoManager);
|
||||
public abstract SortedSet<Integer> localDeviceIdsOf(BareJid localUser);
|
||||
|
||||
/**
|
||||
* Check, if our freshly generated deviceId is available (unique) in our deviceList.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param id our deviceId.
|
||||
* @param userDevice our current device.
|
||||
* @param id deviceId to check for.
|
||||
* @return true if list did not contain our id, else false
|
||||
*/
|
||||
boolean isAvailableDeviceId(OmemoManager omemoManager, int id) {
|
||||
boolean isAvailableDeviceId(OmemoDevice userDevice, int id) {
|
||||
LOGGER.log(Level.INFO, "Check if id " + id + " is available...");
|
||||
|
||||
// Lookup local cached device list
|
||||
BareJid ownJid = omemoManager.getOwnJid();
|
||||
CachedDeviceList cachedDeviceList = loadCachedDeviceList(omemoManager, ownJid);
|
||||
BareJid ownJid = userDevice.getJid();
|
||||
CachedDeviceList cachedDeviceList;
|
||||
|
||||
cachedDeviceList = loadCachedDeviceList(userDevice, ownJid);
|
||||
|
||||
if (cachedDeviceList == null) {
|
||||
cachedDeviceList = new CachedDeviceList();
|
||||
|
@ -95,32 +91,15 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
return !cachedDeviceList.contains(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a new Identity (deviceId, identityKeys, preKeys...).
|
||||
*
|
||||
* @param omemoManager omemoManager of our device we want to regenerate.
|
||||
* @throws CorruptedOmemoKeyException in case something goes wrong
|
||||
*/
|
||||
void regenerate(OmemoManager omemoManager) throws CorruptedOmemoKeyException {
|
||||
LOGGER.log(Level.INFO, "Regenerating with deviceId " + omemoManager.getDeviceId() + "...");
|
||||
int nextPreKeyId = 1;
|
||||
storeOmemoIdentityKeyPair(omemoManager, generateOmemoIdentityKeyPair());
|
||||
storeOmemoPreKeys(omemoManager, generateOmemoPreKeys(nextPreKeyId, TARGET_PRE_KEY_COUNT));
|
||||
storeLastPreKeyId(omemoManager, OmemoKeyUtil.addInBounds(nextPreKeyId, TARGET_PRE_KEY_COUNT));
|
||||
storeCurrentSignedPreKeyId(omemoManager, -1); //Set back to no-value default
|
||||
changeSignedPreKey(omemoManager);
|
||||
initializeOmemoSessions(omemoManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Merge the received OmemoDeviceListElement with the one we already have. If we had none, the received one is saved.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contact Contact we received the list from.
|
||||
* @param list List we received.
|
||||
*/
|
||||
void mergeCachedDeviceList(OmemoManager omemoManager, BareJid contact, OmemoDeviceListElement list) {
|
||||
CachedDeviceList cached = loadCachedDeviceList(omemoManager, contact);
|
||||
void mergeCachedDeviceList(OmemoDevice userDevice, BareJid contact, OmemoDeviceListElement list) {
|
||||
CachedDeviceList cached = loadCachedDeviceList(userDevice, contact);
|
||||
|
||||
if (cached == null) {
|
||||
cached = new CachedDeviceList();
|
||||
|
@ -129,51 +108,56 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
if (list != null) {
|
||||
cached.merge(list.getDeviceIds());
|
||||
}
|
||||
storeCachedDeviceList(omemoManager, contact, cached);
|
||||
storeCachedDeviceList(userDevice, contact, cached);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renew our singed preKey. This should be done once every 7-14 days.
|
||||
* The old signed PreKey should be kept for around a month or so (look it up in the XEP).
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @throws CorruptedOmemoKeyException when our identityKey is invalid
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @throws CorruptedOmemoKeyException when our identityKey is invalid.
|
||||
* @throws IllegalStateException when our IdentityKeyPair is null.
|
||||
*/
|
||||
void changeSignedPreKey(OmemoManager omemoManager) throws CorruptedOmemoKeyException {
|
||||
int lastSignedPreKeyId = loadCurrentSignedPreKeyId(omemoManager);
|
||||
if (lastSignedPreKeyId == -1) lastSignedPreKeyId = 0;
|
||||
try {
|
||||
T_SigPreKey newSignedPreKey = generateOmemoSignedPreKey(loadOmemoIdentityKeyPair(omemoManager), lastSignedPreKeyId + 1);
|
||||
storeOmemoSignedPreKey(omemoManager, lastSignedPreKeyId + 1, newSignedPreKey);
|
||||
storeCurrentSignedPreKeyId(omemoManager, lastSignedPreKeyId + 1);
|
||||
setDateOfLastSignedPreKeyRenewal(omemoManager);
|
||||
removeOldSignedPreKeys(omemoManager);
|
||||
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.INFO, "Couldn't change SignedPreKey: " + e.getMessage());
|
||||
throw e;
|
||||
void changeSignedPreKey(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKeyPair idKeyPair = loadOmemoIdentityKeyPair(userDevice);
|
||||
if (idKeyPair == null) {
|
||||
throw new IllegalStateException("Our IdentityKeyPair is null.");
|
||||
}
|
||||
|
||||
TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);
|
||||
if (signedPreKeys.size() == 0) {
|
||||
T_SigPreKey newKey = generateOmemoSignedPreKey(idKeyPair, 1);
|
||||
storeOmemoSignedPreKey(userDevice, 1, newKey);
|
||||
} else {
|
||||
int lastId = signedPreKeys.lastKey();
|
||||
T_SigPreKey newKey = generateOmemoSignedPreKey(idKeyPair, lastId + 1);
|
||||
storeOmemoSignedPreKey(userDevice, lastId + 1, newKey);
|
||||
}
|
||||
|
||||
setDateOfLastSignedPreKeyRenewal(userDevice, new Date());
|
||||
removeOldSignedPreKeys(userDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the oldest signedPreKey until there are only MAX_NUMBER_OF_STORED_SIGNED_PREKEYS left.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
*/
|
||||
private void removeOldSignedPreKeys(OmemoManager omemoManager) {
|
||||
private void removeOldSignedPreKeys(OmemoDevice userDevice) {
|
||||
if (OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys() <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int currentId = loadCurrentSignedPreKeyId(omemoManager);
|
||||
if (currentId == -1) currentId = 0;
|
||||
HashMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(omemoManager);
|
||||
TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);
|
||||
|
||||
for (int i : signedPreKeys.keySet()) {
|
||||
if (i <= currentId - OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys()) {
|
||||
LOGGER.log(Level.INFO, "Remove signedPreKey " + i + ".");
|
||||
removeOmemoSignedPreKey(omemoManager, i);
|
||||
}
|
||||
for (int i = 0; i < signedPreKeys.keySet().size() - OmemoConfiguration.getMaxNumberOfStoredSignedPreKeys(); i++) {
|
||||
int keyId = signedPreKeys.firstKey();
|
||||
LOGGER.log(Level.INFO, "Remove signedPreKey " + keyId + ".");
|
||||
removeOmemoSignedPreKey(userDevice, i);
|
||||
signedPreKeys = loadOmemoSignedPreKeys(userDevice);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -182,26 +166,36 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* If we used up n preKeys since we last published our bundle, generate n new preKeys and add them to the bundle.
|
||||
* We should always publish TARGET_PRE_KEY_COUNT keys.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return OmemoBundleElement
|
||||
* @throws CorruptedOmemoKeyException when a key could not be loaded
|
||||
*/
|
||||
OmemoBundleVAxolotlElement packOmemoBundle(OmemoManager omemoManager) throws CorruptedOmemoKeyException {
|
||||
int currentSignedPreKeyId = loadCurrentSignedPreKeyId(omemoManager);
|
||||
if (currentSignedPreKeyId == -1) currentSignedPreKeyId = 0;
|
||||
T_SigPreKey currentSignedPreKey = loadOmemoSignedPreKey(omemoManager, currentSignedPreKeyId);
|
||||
T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(omemoManager);
|
||||
OmemoBundleVAxolotlElement packOmemoBundle(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKeyPair identityKeyPair = loadOmemoIdentityKeyPair(userDevice);
|
||||
if (identityKeyPair == null) {
|
||||
identityKeyPair = generateOmemoIdentityKeyPair();
|
||||
storeOmemoIdentityKeyPair(userDevice, identityKeyPair);
|
||||
}
|
||||
|
||||
HashMap<Integer, T_PreKey> preKeys = loadOmemoPreKeys(omemoManager);
|
||||
TreeMap<Integer, T_SigPreKey> signedPreKeys = loadOmemoSignedPreKeys(userDevice);
|
||||
if (signedPreKeys.size() == 0) {
|
||||
changeSignedPreKey(userDevice);
|
||||
signedPreKeys = loadOmemoSignedPreKeys(userDevice);
|
||||
}
|
||||
|
||||
int currentSignedPreKeyId = signedPreKeys.lastKey();
|
||||
T_SigPreKey currentSignedPreKey = signedPreKeys.get(currentSignedPreKeyId);
|
||||
|
||||
TreeMap<Integer, T_PreKey> preKeys = loadOmemoPreKeys(userDevice);
|
||||
int newKeysCount = TARGET_PRE_KEY_COUNT - preKeys.size();
|
||||
int startId = preKeys.size() == 0 ? 0 : preKeys.lastKey();
|
||||
|
||||
if (newKeysCount > 0) {
|
||||
int lastPreKeyId = loadLastPreKeyId(omemoManager);
|
||||
if (lastPreKeyId == -1) lastPreKeyId = 0;
|
||||
HashMap<Integer, T_PreKey> newKeys = generateOmemoPreKeys(lastPreKeyId + 1, newKeysCount);
|
||||
storeOmemoPreKeys(omemoManager, newKeys);
|
||||
TreeMap<Integer, T_PreKey> newKeys = generateOmemoPreKeys(startId + 1, newKeysCount);
|
||||
storeOmemoPreKeys(userDevice, newKeys);
|
||||
preKeys.putAll(newKeys);
|
||||
storeLastPreKeyId(omemoManager, lastPreKeyId + newKeysCount);
|
||||
}
|
||||
|
||||
return new OmemoBundleVAxolotlElement(
|
||||
|
@ -213,159 +207,8 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Preload all OMEMO sessions for our devices and our contacts from existing raw sessions.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
*/
|
||||
void initializeOmemoSessions(OmemoManager omemoManager) {
|
||||
|
||||
// Get HashMap of our omemoSessions
|
||||
HashMap<OmemoDevice, OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>>
|
||||
sessions = omemoSessions.get(omemoManager);
|
||||
if (sessions == null) {
|
||||
sessions = new HashMap<>();
|
||||
omemoSessions.put(omemoManager, sessions);
|
||||
}
|
||||
|
||||
// Sessions with our own devices
|
||||
HashMap<Integer, T_Sess> ourRawSessions = loadAllRawSessionsOf(omemoManager, omemoManager.getOwnJid());
|
||||
ourRawSessions.remove(omemoManager.getDeviceId()); //Just to make sure we have no session with ourselves...
|
||||
sessions.putAll(createOmemoSessionsFromRawSessions(omemoManager, omemoManager.getOwnJid(), ourRawSessions));
|
||||
|
||||
// Sessions with contacts
|
||||
for (RosterEntry rosterEntry : Roster.getInstanceFor(omemoManager.getConnection()).getEntries()) {
|
||||
HashMap<Integer, T_Sess> contactDevices = loadAllRawSessionsOf(omemoManager, rosterEntry.getJid().asBareJid());
|
||||
sessions.putAll(createOmemoSessionsFromRawSessions(omemoManager, rosterEntry.getJid().asBareJid(), contactDevices));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Forget all omemoSessions of the omemoManager from cache.
|
||||
* This will not remove the sessions from persistent memory!
|
||||
*
|
||||
* @param omemoManager omemoManager we want to forget sessions from.
|
||||
*/
|
||||
void forgetOmemoSessions(OmemoManager omemoManager) {
|
||||
omemoSessions.remove(omemoManager);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new concrete OmemoSession with a contact.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device to establish the session with
|
||||
* @param identityKey identityKey of the device
|
||||
* @return concrete OmemoSession
|
||||
*/
|
||||
private OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
createOmemoSession(OmemoManager omemoManager, OmemoDevice device, T_IdKey identityKey) {
|
||||
return keyUtil().createOmemoSession(omemoManager, this, device, identityKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the OmemoSession for the OmemoDevice. If there is no OmemoSession for the device yet,
|
||||
* build one from local raw session material.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device OmemoDevice
|
||||
* @return OmemoSession
|
||||
*/
|
||||
public OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
getOmemoSessionOf(OmemoManager omemoManager, OmemoDevice device) {
|
||||
|
||||
HashMap<OmemoDevice, OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>>
|
||||
sessions = omemoSessions.get(omemoManager);
|
||||
|
||||
if (sessions == null) {
|
||||
sessions = new HashMap<>();
|
||||
omemoSessions.put(omemoManager, sessions);
|
||||
}
|
||||
|
||||
OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
session = sessions.get(device);
|
||||
// No OmemoSession found
|
||||
if (session == null) {
|
||||
T_IdKey identityKey = null;
|
||||
try {
|
||||
identityKey = loadOmemoIdentityKey(omemoManager, device);
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.WARNING, "getOmemoSessionOf could not load identityKey of " + device + ": " + e.getMessage());
|
||||
}
|
||||
|
||||
if (identityKey != null) {
|
||||
session = createOmemoSession(omemoManager, device, identityKey);
|
||||
|
||||
} else {
|
||||
LOGGER.log(Level.INFO, "getOmemoSessionOf couldn't find an identityKey for " + device
|
||||
+ ". Initiate session without.");
|
||||
session = createOmemoSession(omemoManager, device, null);
|
||||
}
|
||||
|
||||
sessions.put(device, session);
|
||||
}
|
||||
|
||||
if (session.getIdentityKey() == null) {
|
||||
try {
|
||||
session.setIdentityKey(loadOmemoIdentityKey(omemoManager, device));
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.WARNING, "Can't update IdentityKey of " + device + ": " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return session;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create OmemoSession objects for all T_Sess objects of the contact.
|
||||
* The T_Sess objects will be wrapped inside a OmemoSession for every device of the contact.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param contact BareJid of the contact
|
||||
* @param rawSessions HashMap of Integers (deviceIds) and T_Sess sessions.
|
||||
* @return HashMap of OmemoContacts and OmemoSessions
|
||||
*/
|
||||
private HashMap<OmemoDevice, OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>>
|
||||
createOmemoSessionsFromRawSessions(OmemoManager omemoManager, BareJid contact, HashMap<Integer, T_Sess> rawSessions) {
|
||||
|
||||
HashMap<OmemoDevice, OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>>
|
||||
sessions = new HashMap<>();
|
||||
|
||||
for (Map.Entry<Integer, T_Sess> sessionEntry : rawSessions.entrySet()) {
|
||||
OmemoDevice omemoDevice = new OmemoDevice(contact, sessionEntry.getKey());
|
||||
try {
|
||||
T_IdKey identityKey = loadOmemoIdentityKey(omemoManager, omemoDevice);
|
||||
if (identityKey != null) {
|
||||
sessions.put(omemoDevice, createOmemoSession(omemoManager, omemoDevice, identityKey));
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, "IdentityKey of " + omemoDevice + " is null. Is this even possible at this point?");
|
||||
}
|
||||
} catch (CorruptedOmemoKeyException e1) {
|
||||
LOGGER.log(Level.WARNING, "buildOmemoSessionFor could not create a session for " + omemoDevice +
|
||||
": " + e1.getMessage());
|
||||
}
|
||||
}
|
||||
return sessions;
|
||||
}
|
||||
|
||||
// *sigh*
|
||||
|
||||
/**
|
||||
* Return the id of the last generated preKey.
|
||||
* This is used to generate new preKeys without preKeyId collisions.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @return id of the last preKey
|
||||
*/
|
||||
public abstract int loadLastPreKeyId(OmemoManager omemoManager);
|
||||
|
||||
/**
|
||||
* Store the id of the last preKey we generated.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param currentPreKeyId the id of the last generated PreKey
|
||||
*/
|
||||
public abstract void storeLastPreKeyId(OmemoManager omemoManager, int currentPreKeyId);
|
||||
|
||||
/**
|
||||
* Generate a new IdentityKeyPair. We should always have only one pair and usually keep this for a long time.
|
||||
*
|
||||
|
@ -378,150 +221,89 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
/**
|
||||
* Load our identityKeyPair from storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return identityKeyPair
|
||||
* @throws CorruptedOmemoKeyException Thrown, if the stored key is damaged (*hands up* not my fault!)
|
||||
*/
|
||||
public abstract T_IdKeyPair loadOmemoIdentityKeyPair(OmemoManager omemoManager) throws CorruptedOmemoKeyException;
|
||||
public abstract T_IdKeyPair loadOmemoIdentityKeyPair(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException;
|
||||
|
||||
/**
|
||||
* Store our identityKeyPair in storage. It would be a cool feature, if the key could be stored in a encrypted
|
||||
* database or something similar.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param identityKeyPair identityKeyPair
|
||||
*/
|
||||
public abstract void storeOmemoIdentityKeyPair(OmemoManager omemoManager, T_IdKeyPair identityKeyPair);
|
||||
public abstract void storeOmemoIdentityKeyPair(OmemoDevice userDevice, T_IdKeyPair identityKeyPair);
|
||||
|
||||
/**
|
||||
* Load the public identityKey of the device.
|
||||
* Remove the identityKeyPair of a user.
|
||||
* @param userDevice our device.
|
||||
*/
|
||||
public abstract void removeOmemoIdentityKeyPair(OmemoDevice userDevice);
|
||||
|
||||
/**
|
||||
* Load the public identityKey of a device.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice the device of which we want to load the identityKey.
|
||||
* @return identityKey
|
||||
* @throws CorruptedOmemoKeyException when the key in question is corrupted and cant be deserialized.
|
||||
*/
|
||||
public abstract T_IdKey loadOmemoIdentityKey(OmemoManager omemoManager, OmemoDevice device) throws CorruptedOmemoKeyException;
|
||||
public abstract T_IdKey loadOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice)
|
||||
throws CorruptedOmemoKeyException;
|
||||
|
||||
/**
|
||||
* Store the public identityKey of the device.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param key identityKey
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice device.
|
||||
* @param contactsKey identityKey belonging to the contactsDevice.
|
||||
*/
|
||||
public abstract void storeOmemoIdentityKey(OmemoManager omemoManager, OmemoDevice device, T_IdKey key);
|
||||
public abstract void storeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice, T_IdKey contactsKey);
|
||||
|
||||
/**
|
||||
* Decide, whether a identityKey of a device is trusted or not.
|
||||
* If you want to use this module, you should memorize, whether the user has trusted this key or not, since
|
||||
* the owner of the identityKey will be able to read sent messages when this method returned 'true' for their
|
||||
* identityKey. Either you let the user decide whether you trust a key every time you see a new key, or you
|
||||
* implement something like 'blind trust' (see https://gultsch.de/trust.html).
|
||||
* Removes the identityKey of a device.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device Owner of the key
|
||||
* @param identityKey identityKey
|
||||
* @return true, if the user trusts the key and wants to send messages to it, otherwise false
|
||||
* @param userDevice our omemoDevice.
|
||||
* @param contactsDevice device of which we want to delete the identityKey.
|
||||
*/
|
||||
public boolean isTrustedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, T_IdKey identityKey) {
|
||||
return isTrustedOmemoIdentity(omemoManager, device, keyUtil().getFingerprint(identityKey));
|
||||
}
|
||||
|
||||
public abstract boolean isTrustedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint);
|
||||
public abstract void removeOmemoIdentityKey(OmemoDevice userDevice, OmemoDevice contactsDevice);
|
||||
|
||||
/**
|
||||
* Did the user yet made a decision about whether to trust or distrust this device?
|
||||
* Set the date in millis of the last message that was received from a device.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param identityKey IdentityKey
|
||||
* @return true, if the user either trusted or distrusted the device. Return false, if the user did not yet decide.
|
||||
*/
|
||||
public boolean isDecidedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, T_IdKey identityKey) {
|
||||
return isDecidedOmemoIdentity(omemoManager, device, keyUtil().getFingerprint(identityKey));
|
||||
}
|
||||
|
||||
public abstract boolean isDecidedOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint);
|
||||
|
||||
/**
|
||||
* Trust an OmemoIdentity. This involves marking the key as trusted.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param identityKey identityKey
|
||||
*/
|
||||
public void trustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, T_IdKey identityKey) {
|
||||
trustOmemoIdentity(omemoManager, device, keyUtil().getFingerprint(identityKey));
|
||||
}
|
||||
|
||||
public abstract void trustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint identityKeyFingerprint);
|
||||
|
||||
/**
|
||||
* Distrust an OmemoIdentity. This involved marking the key as distrusted.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param identityKey identityKey
|
||||
*/
|
||||
public void distrustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, T_IdKey identityKey) {
|
||||
distrustOmemoIdentity(omemoManager, device, keyUtil().getFingerprint(identityKey));
|
||||
}
|
||||
|
||||
public abstract void distrustOmemoIdentity(OmemoManager omemoManager, OmemoDevice device, OmemoFingerprint fingerprint);
|
||||
|
||||
/**
|
||||
* Set the date in millis of the last message that was received from device 'from' to 'date'.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param from device in question
|
||||
* @param userDevice omemoManager of our device.
|
||||
* @param contactsDevice device in question
|
||||
* @param date date of the last received message
|
||||
*/
|
||||
public abstract void setDateOfLastReceivedMessage(OmemoManager omemoManager, OmemoDevice from, Date date);
|
||||
|
||||
/**
|
||||
* Set the date in millis of the last message that was received from device 'from' to now.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param from device in question
|
||||
*/
|
||||
public void setDateOfLastReceivedMessage(OmemoManager omemoManager, OmemoDevice from) {
|
||||
this.setDateOfLastReceivedMessage(omemoManager, from, new Date());
|
||||
}
|
||||
public abstract void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date);
|
||||
|
||||
/**
|
||||
* Return the date in millis of the last message that was received from device 'from'.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param from device in question
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice device in question
|
||||
* @return date if existent as long, otherwise -1
|
||||
*/
|
||||
public abstract Date getDateOfLastReceivedMessage(OmemoManager omemoManager, OmemoDevice from);
|
||||
public abstract Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice);
|
||||
|
||||
/**
|
||||
* Set the date in millis of the last time the signed preKey was renewed.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param date date
|
||||
*/
|
||||
public abstract void setDateOfLastSignedPreKeyRenewal(OmemoManager omemoManager, Date date);
|
||||
|
||||
/**
|
||||
* Store the date of the last preKey renewal in the omemoStore.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
*/
|
||||
public void setDateOfLastSignedPreKeyRenewal(OmemoManager omemoManager) {
|
||||
setDateOfLastSignedPreKeyRenewal(omemoManager, new Date());
|
||||
}
|
||||
public abstract void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date);
|
||||
|
||||
/**
|
||||
* Get the date in millis of the last time the signed preKey was renewed.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return date if existent, otherwise null
|
||||
*/
|
||||
public abstract Date getDateOfLastSignedPreKeyRenewal(OmemoManager omemoManager);
|
||||
public abstract Date getDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice);
|
||||
|
||||
/**
|
||||
* Generate 'count' new PreKeys beginning with id 'startId'.
|
||||
|
@ -531,37 +313,37 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @param count how many keys do we want to generate
|
||||
* @return Map of new preKeys
|
||||
*/
|
||||
public HashMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count) {
|
||||
public TreeMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count) {
|
||||
return keyUtil().generateOmemoPreKeys(startId, count);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the preKey with id 'preKeyId' from storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param preKeyId id of the key to be loaded
|
||||
* @return loaded preKey
|
||||
*/
|
||||
public abstract T_PreKey loadOmemoPreKey(OmemoManager omemoManager, int preKeyId);
|
||||
public abstract T_PreKey loadOmemoPreKey(OmemoDevice userDevice, int preKeyId);
|
||||
|
||||
/**
|
||||
* Store a PreKey in storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param preKeyId id of the key
|
||||
* @param preKey key
|
||||
*/
|
||||
public abstract void storeOmemoPreKey(OmemoManager omemoManager, int preKeyId, T_PreKey preKey);
|
||||
public abstract void storeOmemoPreKey(OmemoDevice userDevice, int preKeyId, T_PreKey preKey);
|
||||
|
||||
/**
|
||||
* Store a whole bunch of preKeys.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param preKeyHashMap HashMap of preKeys
|
||||
*/
|
||||
public void storeOmemoPreKeys(OmemoManager omemoManager, HashMap<Integer, T_PreKey> preKeyHashMap) {
|
||||
for (Map.Entry<Integer, T_PreKey> e : preKeyHashMap.entrySet()) {
|
||||
storeOmemoPreKey(omemoManager, e.getKey(), e.getValue());
|
||||
public void storeOmemoPreKeys(OmemoDevice userDevice, TreeMap<Integer, T_PreKey> preKeyHashMap) {
|
||||
for (Map.Entry<Integer, T_PreKey> entry : preKeyHashMap.entrySet()) {
|
||||
storeOmemoPreKey(userDevice, entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -569,52 +351,35 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* remove a preKey from storage. This is called, when a contact used one of our preKeys to establish a session
|
||||
* with us.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param preKeyId id of the used key that will be deleted
|
||||
*/
|
||||
public abstract void removeOmemoPreKey(OmemoManager omemoManager, int preKeyId);
|
||||
|
||||
/**
|
||||
* Return the id of the currently used signed preKey.
|
||||
* This is used to avoid collisions when generating a new signedPreKey.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @return id
|
||||
*/
|
||||
public abstract int loadCurrentSignedPreKeyId(OmemoManager omemoManager);
|
||||
|
||||
/**
|
||||
* Store the id of the currently used signedPreKey.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param currentSignedPreKeyId if of the signedPreKey that is currently in use
|
||||
*/
|
||||
public abstract void storeCurrentSignedPreKeyId(OmemoManager omemoManager, int currentSignedPreKeyId);
|
||||
public abstract void removeOmemoPreKey(OmemoDevice userDevice, int preKeyId);
|
||||
|
||||
/**
|
||||
* Return all our current OmemoPreKeys.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return Map containing our preKeys
|
||||
*/
|
||||
public abstract HashMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoManager omemoManager);
|
||||
public abstract TreeMap<Integer, T_PreKey> loadOmemoPreKeys(OmemoDevice userDevice);
|
||||
|
||||
/**
|
||||
* Return the signedPreKey with the id 'singedPreKeyId'.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param signedPreKeyId id of the key
|
||||
* @return key
|
||||
*/
|
||||
public abstract T_SigPreKey loadOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId);
|
||||
public abstract T_SigPreKey loadOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId);
|
||||
|
||||
/**
|
||||
* Load all our signed PreKeys.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return HashMap of our singedPreKeys
|
||||
*/
|
||||
public abstract HashMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoManager omemoManager);
|
||||
public abstract TreeMap<Integer, T_SigPreKey> loadOmemoSignedPreKeys(OmemoDevice userDevice);
|
||||
|
||||
/**
|
||||
* Generate a new signed preKey.
|
||||
|
@ -624,105 +389,109 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @return signedPreKey
|
||||
* @throws CorruptedOmemoKeyException when something goes wrong
|
||||
*/
|
||||
public T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId) throws CorruptedOmemoKeyException {
|
||||
public T_SigPreKey generateOmemoSignedPreKey(T_IdKeyPair identityKeyPair, int signedPreKeyId)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
return keyUtil().generateOmemoSignedPreKey(identityKeyPair, signedPreKeyId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a signedPreKey in storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param signedPreKeyId id of the signedPreKey
|
||||
* @param signedPreKey the key itself
|
||||
*/
|
||||
public abstract void storeOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId, T_SigPreKey signedPreKey);
|
||||
public abstract void storeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId, T_SigPreKey signedPreKey);
|
||||
|
||||
/**
|
||||
* Remove a signedPreKey from storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param signedPreKeyId id of the key that will be removed
|
||||
*/
|
||||
public abstract void removeOmemoSignedPreKey(OmemoManager omemoManager, int signedPreKeyId);
|
||||
public abstract void removeOmemoSignedPreKey(OmemoDevice userDevice, int signedPreKeyId);
|
||||
|
||||
/**
|
||||
* Load the crypto-lib specific session object of the device from storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device whose session we want to load
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice device whose session we want to load
|
||||
* @return crypto related session
|
||||
*/
|
||||
public abstract T_Sess loadRawSession(OmemoManager omemoManager, OmemoDevice device);
|
||||
public abstract T_Sess loadRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice);
|
||||
|
||||
/**
|
||||
* Load all crypto-lib specific session objects of contact 'contact'.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contact BareJid of the contact we want to get all sessions from
|
||||
* @return HashMap of deviceId and sessions of the contact
|
||||
* @return TreeMap of deviceId and sessions of the contact
|
||||
*/
|
||||
public abstract HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoManager omemoManager, BareJid contact);
|
||||
public abstract HashMap<Integer, T_Sess> loadAllRawSessionsOf(OmemoDevice userDevice, BareJid contact);
|
||||
|
||||
/**
|
||||
* Store a crypto-lib specific session to storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device OmemoDevice whose session we want to store
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice OmemoDevice whose session we want to store
|
||||
* @param session session
|
||||
*/
|
||||
public abstract void storeRawSession(OmemoManager omemoManager, OmemoDevice device, T_Sess session);
|
||||
public abstract void storeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice, T_Sess session);
|
||||
|
||||
/**
|
||||
* Remove a crypto-lib specific session from storage.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device whose session we want to delete
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice device whose session we want to delete
|
||||
*/
|
||||
public abstract void removeRawSession(OmemoManager omemoManager, OmemoDevice device);
|
||||
public abstract void removeRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice);
|
||||
|
||||
/**
|
||||
* Remove all crypto-lib specific session of a contact.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contact BareJid of the contact
|
||||
*/
|
||||
public abstract void removeAllRawSessionsOf(OmemoManager omemoManager, BareJid contact);
|
||||
public abstract void removeAllRawSessionsOf(OmemoDevice userDevice, BareJid contact);
|
||||
|
||||
/**
|
||||
* Return true, if we have a session with the device, otherwise false.
|
||||
* Hint for Signal: Do not try 'return getSession() != null' since this will create a new session.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contactsDevice device
|
||||
* @return true if we have session, otherwise false
|
||||
*/
|
||||
public abstract boolean containsRawSession(OmemoManager omemoManager, OmemoDevice device);
|
||||
public abstract boolean containsRawSession(OmemoDevice userDevice, OmemoDevice contactsDevice);
|
||||
|
||||
/**
|
||||
* Load a list of deviceIds from contact 'contact' from the local cache.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contact contact we want to get the deviceList of
|
||||
* @return CachedDeviceList of the contact
|
||||
*/
|
||||
public abstract CachedDeviceList loadCachedDeviceList(OmemoManager omemoManager, BareJid contact);
|
||||
public abstract CachedDeviceList loadCachedDeviceList(OmemoDevice userDevice, BareJid contact);
|
||||
|
||||
/**
|
||||
* Store the DeviceList of the contact in local storage.
|
||||
* See this as a cache.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @param contact Contact
|
||||
* @param deviceList list of the contacts devices' ids.
|
||||
* @param contactsDeviceList list of the contacts devices' ids.
|
||||
*/
|
||||
public abstract void storeCachedDeviceList(OmemoManager omemoManager, BareJid contact, CachedDeviceList deviceList);
|
||||
public abstract void storeCachedDeviceList(OmemoDevice userDevice,
|
||||
BareJid contact,
|
||||
CachedDeviceList contactsDeviceList);
|
||||
|
||||
/**
|
||||
* Delete this device's IdentityKey, PreKeys, SignedPreKeys and Sessions.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
*/
|
||||
public abstract void purgeOwnDeviceKeys(OmemoManager omemoManager);
|
||||
public abstract void purgeOwnDeviceKeys(OmemoDevice userDevice);
|
||||
|
||||
/**
|
||||
* Return a concrete KeyUtil object that we can use as a utility to create keys etc.
|
||||
|
@ -734,58 +503,43 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
/**
|
||||
* Return our identityKeys fingerprint.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param userDevice our OmemoDevice.
|
||||
* @return fingerprint of our identityKeyPair
|
||||
*/
|
||||
public OmemoFingerprint getFingerprint(OmemoManager omemoManager) {
|
||||
try {
|
||||
return keyUtil().getFingerprint(keyUtil().identityKeyFromPair(loadOmemoIdentityKeyPair(omemoManager)));
|
||||
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.WARNING, "getFingerprint failed due to corrupted identityKeyPair: " + e.getMessage());
|
||||
public OmemoFingerprint getFingerprint(OmemoDevice userDevice)
|
||||
throws CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKeyPair keyPair = loadOmemoIdentityKeyPair(userDevice);
|
||||
if (keyPair == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return keyUtil().getFingerprintOfIdentityKey(keyUtil().identityKeyFromPair(keyPair));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default deviceId for a user.
|
||||
* The defaultDeviceId will be used when the OmemoManager gets instantiated without passing a specific deviceId.
|
||||
* If no default id is set, return -1;
|
||||
*
|
||||
* @param user user
|
||||
* @return defaultDeviceId or -1
|
||||
*/
|
||||
public abstract int getDefaultDeviceId(BareJid user);
|
||||
|
||||
/**
|
||||
* Set the default deviceId of a user.
|
||||
*
|
||||
* @param user user
|
||||
* @param defaultDeviceId defaultDeviceId
|
||||
*/
|
||||
public abstract void setDefaultDeviceId(BareJid user, int defaultDeviceId);
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the given devices announced identityKey.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param device device
|
||||
* @param managerGuard omemoManager of our device.
|
||||
* @param contactsDevice device
|
||||
* @throws CannotEstablishOmemoSessionException if we cannot establish a session
|
||||
* @return fingerprint of the identityKey
|
||||
*/
|
||||
public OmemoFingerprint getFingerprint(OmemoManager omemoManager, OmemoDevice device) throws CannotEstablishOmemoSessionException {
|
||||
T_IdKey idKey;
|
||||
public OmemoFingerprint getFingerprint(OmemoManager.KnownBareJidGuard managerGuard, OmemoDevice contactsDevice)
|
||||
throws CannotEstablishOmemoSessionException, CorruptedOmemoKeyException
|
||||
{
|
||||
OmemoManager omemoManager = managerGuard.get();
|
||||
T_IdKey identityKey = loadOmemoIdentityKey(omemoManager.getOwnDevice(), contactsDevice);
|
||||
if (identityKey == null) {
|
||||
// Key cannot be loaded. Maybe it doesn't exist. Fetch a bundle to get it...
|
||||
OmemoService.getInstance().buildSessionWithDevice(managerGuard, contactsDevice, true);
|
||||
}
|
||||
|
||||
try {
|
||||
idKey = loadOmemoIdentityKey(omemoManager, device);
|
||||
if (idKey == null) {
|
||||
OmemoService.getInstance().buildSessionFromOmemoBundle(omemoManager, device, true);
|
||||
}
|
||||
idKey = loadOmemoIdentityKey(omemoManager, device);
|
||||
} catch (CorruptedOmemoKeyException e) {
|
||||
LOGGER.log(Level.WARNING, "getFingerprint failed due to corrupted identityKey: " + e.getMessage());
|
||||
identityKey = loadOmemoIdentityKey(omemoManager.getOwnDevice(), contactsDevice);
|
||||
if (identityKey == null) {
|
||||
return null;
|
||||
}
|
||||
return keyUtil().getFingerprint(idKey);
|
||||
|
||||
return keyUtil().getFingerprintOfIdentityKey(identityKey);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
*
|
||||
* 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.exceptions;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
public class MultipleIOException extends IOException {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final ArrayList<IOException> exceptions = new ArrayList<>();
|
||||
|
||||
public MultipleIOException(IOException... exceptions) {
|
||||
this.exceptions.addAll(Arrays.asList(exceptions));
|
||||
}
|
||||
|
||||
public void addException(IOException e) {
|
||||
exceptions.add(e);
|
||||
}
|
||||
|
||||
public ArrayList<IOException> getExceptions() {
|
||||
return exceptions;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
/**
|
||||
*
|
||||
* 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.exceptions;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
/**
|
||||
* Exception that gets thrown when we try to decrypt a message which contains an identityKey that differs from the one
|
||||
* we previously trusted.
|
||||
*/
|
||||
public class UntrustedOmemoIdentityException extends Exception {
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
private final OmemoDevice device;
|
||||
private final OmemoFingerprint trustedKey, untrustedKey;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
* @param device device which sent the message.
|
||||
* @param fpTrusted fingerprint of the identityKey we previously had and trusted.
|
||||
* @param fpUntrusted fingerprint of the new key which is untrusted.
|
||||
*/
|
||||
public UntrustedOmemoIdentityException(OmemoDevice device, OmemoFingerprint fpTrusted, OmemoFingerprint fpUntrusted) {
|
||||
super();
|
||||
this.device = device;
|
||||
this.trustedKey = fpTrusted;
|
||||
this.untrustedKey = fpUntrusted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the device which sent the message.
|
||||
* @return omemoDevice.
|
||||
*/
|
||||
public OmemoDevice getDevice() {
|
||||
return device;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the key we expected.
|
||||
* @return
|
||||
*/
|
||||
public OmemoFingerprint getTrustedFingerprint() {
|
||||
return trustedKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the unexpected untrusted key.
|
||||
* @return
|
||||
*/
|
||||
public OmemoFingerprint getUntrustedFingerprint() {
|
||||
return untrustedKey;
|
||||
}
|
||||
}
|
|
@ -43,6 +43,16 @@ public class CachedDeviceList implements Serializable {
|
|||
this.inactiveDevices = new HashSet<>();
|
||||
}
|
||||
|
||||
public CachedDeviceList(Set<Integer> activeDevices, Set<Integer> inactiveDevices) {
|
||||
this();
|
||||
this.activeDevices.addAll(activeDevices);
|
||||
this.inactiveDevices.addAll(inactiveDevices);
|
||||
}
|
||||
|
||||
public CachedDeviceList(CachedDeviceList original) {
|
||||
this(original.getActiveDevices(), original.getInactiveDevices());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all active devices.
|
||||
* Active devices are all devices that were in the latest DeviceList update.
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
|
||||
/**
|
||||
* Class that contains information about a decrypted message (eg. which key was used, if it was a carbon...).
|
||||
*
|
||||
|
@ -23,7 +25,7 @@ package org.jivesoftware.smackx.omemo.internal;
|
|||
*/
|
||||
public class OmemoMessageInformation {
|
||||
private boolean isOmemoMessage;
|
||||
private IdentityKeyWrapper senderIdentityKey;
|
||||
private OmemoFingerprint senderFingerprint;
|
||||
private OmemoDevice senderDevice;
|
||||
private CARBON carbon = CARBON.NONE;
|
||||
|
||||
|
@ -37,12 +39,12 @@ public class OmemoMessageInformation {
|
|||
/**
|
||||
* Creates a new OmemoMessageInformation object. Its assumed, that this is about an OMEMO message.
|
||||
*
|
||||
* @param senderIdentityKey identityKey of the sender device
|
||||
* @param senderFingerprint fingerprint of the identityKey of the sender device
|
||||
* @param senderDevice device that sent the message
|
||||
* @param carbon Carbon type
|
||||
*/
|
||||
public OmemoMessageInformation(IdentityKeyWrapper senderIdentityKey, OmemoDevice senderDevice, CARBON carbon) {
|
||||
this.senderIdentityKey = senderIdentityKey;
|
||||
public OmemoMessageInformation(OmemoFingerprint senderFingerprint, OmemoDevice senderDevice, CARBON carbon) {
|
||||
this.senderFingerprint = senderFingerprint;
|
||||
this.senderDevice = senderDevice;
|
||||
this.carbon = carbon;
|
||||
this.isOmemoMessage = true;
|
||||
|
@ -51,13 +53,13 @@ public class OmemoMessageInformation {
|
|||
/**
|
||||
* Create a new OmemoMessageInformation.
|
||||
*
|
||||
* @param senderIdentityKey identityKey of the sender device
|
||||
* @param senderFingerprint fingerprint of the identityKey of the sender device
|
||||
* @param senderDevice device that sent the message
|
||||
* @param carbon Carbon type
|
||||
* @param omemo is this an omemo message?
|
||||
*/
|
||||
public OmemoMessageInformation(IdentityKeyWrapper senderIdentityKey, OmemoDevice senderDevice, CARBON carbon, boolean omemo) {
|
||||
this(senderIdentityKey, senderDevice, carbon);
|
||||
public OmemoMessageInformation(OmemoFingerprint senderFingerprint, OmemoDevice senderDevice, CARBON carbon, boolean omemo) {
|
||||
this(senderFingerprint, senderDevice, carbon);
|
||||
this.isOmemoMessage = omemo;
|
||||
}
|
||||
|
||||
|
@ -66,17 +68,17 @@ public class OmemoMessageInformation {
|
|||
*
|
||||
* @return identityKey
|
||||
*/
|
||||
public IdentityKeyWrapper getSenderIdentityKey() {
|
||||
return senderIdentityKey;
|
||||
public OmemoFingerprint getSenderFingerprint() {
|
||||
return senderFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the sender devices identityKey.
|
||||
*
|
||||
* @param senderIdentityKey identityKey
|
||||
* @param senderFingerprint fingerprint of the senders identityKey.
|
||||
*/
|
||||
public void setSenderIdentityKey(IdentityKeyWrapper senderIdentityKey) {
|
||||
this.senderIdentityKey = senderIdentityKey;
|
||||
public void setSenderFingerprint(OmemoFingerprint senderFingerprint) {
|
||||
this.senderFingerprint = senderFingerprint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,266 +0,0 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
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.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement.OmemoHeader.Key;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.MultipleCryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.NoRawSessionException;
|
||||
|
||||
/**
|
||||
* This class represents a OMEMO session between us and another device.
|
||||
*
|
||||
* @param <T_IdKeyPair> IdentityKeyPair class
|
||||
* @param <T_IdKey> IdentityKey class
|
||||
* @param <T_PreKey> PreKey class
|
||||
* @param <T_SigPreKey> SignedPreKey class
|
||||
* @param <T_Sess> Session class
|
||||
* @param <T_Addr> Address class
|
||||
* @param <T_ECPub> Elliptic Curve PublicKey class
|
||||
* @param <T_Bundle> Bundle class
|
||||
* @param <T_Ciph> Cipher class
|
||||
* @author Paul Schaub
|
||||
*/
|
||||
public abstract class OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||
|
||||
protected final T_Ciph cipher;
|
||||
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
||||
protected final OmemoDevice remoteDevice;
|
||||
protected final OmemoManager omemoManager;
|
||||
protected T_IdKey identityKey;
|
||||
protected int preKeyId = -1;
|
||||
|
||||
/**
|
||||
* Constructor used when we establish the session.
|
||||
*
|
||||
* @param omemoManager OmemoManager of our device
|
||||
* @param omemoStore OmemoStore where we want to store the session and get key information from
|
||||
* @param remoteDevice the OmemoDevice we want to establish the session with
|
||||
* @param identityKey identityKey of the recipient
|
||||
*/
|
||||
public OmemoSession(OmemoManager omemoManager,
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore,
|
||||
OmemoDevice remoteDevice, T_IdKey identityKey) {
|
||||
this(omemoManager, omemoStore, remoteDevice);
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Another constructor used when they establish the session with us.
|
||||
*
|
||||
* @param omemoManager OmemoManager of our device
|
||||
* @param omemoStore OmemoStore we want to store the session and their key in
|
||||
* @param remoteDevice identityKey of the partner
|
||||
*/
|
||||
public OmemoSession(OmemoManager omemoManager, OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore,
|
||||
OmemoDevice remoteDevice) {
|
||||
this.omemoManager = omemoManager;
|
||||
this.omemoStore = omemoStore;
|
||||
this.remoteDevice = remoteDevice;
|
||||
this.cipher = createCipher(remoteDevice);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decrypt the transported message key using the double ratchet session.
|
||||
*
|
||||
* @param element omemoElement
|
||||
* @param keyId our keyId
|
||||
* @return tuple of cipher generated from the unpacked message key and the authtag
|
||||
* @throws CryptoFailedException if decryption using the double ratchet fails
|
||||
* @throws NoRawSessionException if we have no session, but the element was NOT a PreKeyMessage
|
||||
*/
|
||||
public CipherAndAuthTag decryptTransportedKey(OmemoElement element, int keyId) throws CryptoFailedException,
|
||||
NoRawSessionException {
|
||||
byte[] unpackedKey = null;
|
||||
List<CryptoFailedException> decryptExceptions = new ArrayList<>();
|
||||
List<Key> keys = element.getHeader().getKeys();
|
||||
// Find key with our ID.
|
||||
for (OmemoElement.OmemoHeader.Key k : keys) {
|
||||
if (k.getId() == keyId) {
|
||||
try {
|
||||
unpackedKey = decryptMessageKey(k.getData());
|
||||
break;
|
||||
} catch (CryptoFailedException e) {
|
||||
// There might be multiple keys with our id, but we can only decrypt one.
|
||||
// So we can't throw the exception, when decrypting the first duplicate which is not for us.
|
||||
decryptExceptions.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unpackedKey == null) {
|
||||
if (!decryptExceptions.isEmpty()) {
|
||||
throw MultipleCryptoFailedException.from(decryptExceptions);
|
||||
}
|
||||
|
||||
throw new CryptoFailedException("Transported key could not be decrypted, since no provided message key. Provides keys: " + keys);
|
||||
}
|
||||
|
||||
byte[] messageKey = new byte[16];
|
||||
byte[] authTag = null;
|
||||
|
||||
if (unpackedKey.length == 32) {
|
||||
authTag = new byte[16];
|
||||
// copy key part into messageKey
|
||||
System.arraycopy(unpackedKey, 0, messageKey, 0, 16);
|
||||
// copy tag part into authTag
|
||||
System.arraycopy(unpackedKey, 16, authTag, 0,16);
|
||||
} else if (element.isKeyTransportElement() && unpackedKey.length == 16) {
|
||||
messageKey = unpackedKey;
|
||||
} else {
|
||||
throw new CryptoFailedException("MessageKey has wrong length: "
|
||||
+ unpackedKey.length + ". Probably legacy auth tag format.");
|
||||
}
|
||||
|
||||
return new CipherAndAuthTag(messageKey, element.getHeader().getIv(), authTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use the symmetric key in cipherAndAuthTag to decrypt the payload of the omemoMessage.
|
||||
* The decrypted payload will be the body of the returned Message.
|
||||
*
|
||||
* @param element omemoElement containing a payload.
|
||||
* @param cipherAndAuthTag cipher and authentication tag.
|
||||
* @return Message containing the decrypted payload in its body.
|
||||
* @throws CryptoFailedException
|
||||
*/
|
||||
public static Message decryptMessageElement(OmemoElement element, CipherAndAuthTag cipherAndAuthTag) throws CryptoFailedException {
|
||||
if (!element.isMessageElement()) {
|
||||
throw new IllegalArgumentException("decryptMessageElement cannot decrypt OmemoElement which is no MessageElement!");
|
||||
}
|
||||
|
||||
if (cipherAndAuthTag.getAuthTag() == null || cipherAndAuthTag.getAuthTag().length != 16) {
|
||||
throw new CryptoFailedException("AuthenticationTag is null or has wrong length: "
|
||||
+ (cipherAndAuthTag.getAuthTag() == null ? "null" : cipherAndAuthTag.getAuthTag().length));
|
||||
}
|
||||
byte[] encryptedBody = new byte[element.getPayload().length + 16];
|
||||
byte[] payload = element.getPayload();
|
||||
System.arraycopy(payload, 0, encryptedBody, 0, payload.length);
|
||||
System.arraycopy(cipherAndAuthTag.getAuthTag(), 0, encryptedBody, payload.length, 16);
|
||||
|
||||
try {
|
||||
String plaintext = new String(cipherAndAuthTag.getCipher().doFinal(encryptedBody), StringUtils.UTF8);
|
||||
Message decrypted = new Message();
|
||||
decrypted.setBody(plaintext);
|
||||
return decrypted;
|
||||
|
||||
} catch (UnsupportedEncodingException | IllegalBlockSizeException | BadPaddingException e) {
|
||||
throw new CryptoFailedException("decryptMessageElement could not decipher message body: "
|
||||
+ e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to decrypt the message.
|
||||
* First decrypt the message key using our session with the sender.
|
||||
* Second use the decrypted key to decrypt the message.
|
||||
* The decrypted content of the 'encrypted'-element becomes the body of the clear text message.
|
||||
*
|
||||
* @param element OmemoElement
|
||||
* @param keyId the key we want to decrypt (usually our own device id)
|
||||
* @return message as plaintext
|
||||
* @throws CryptoFailedException
|
||||
* @throws NoRawSessionException
|
||||
*/
|
||||
// TODO find solution for what we actually want to decrypt (String, Message, List<ExtensionElements>...)
|
||||
public Message decryptMessageElement(OmemoElement element, int keyId) throws CryptoFailedException, NoRawSessionException {
|
||||
if (!element.isMessageElement()) {
|
||||
throw new IllegalArgumentException("OmemoElement is not a messageElement!");
|
||||
}
|
||||
|
||||
CipherAndAuthTag cipherAndAuthTag = decryptTransportedKey(element, keyId);
|
||||
return decryptMessageElement(element, cipherAndAuthTag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new SessionCipher used to encrypt/decrypt keys. The cipher typically implements the ratchet and KDF-chains.
|
||||
*
|
||||
* @param contact OmemoDevice
|
||||
* @return SessionCipher
|
||||
*/
|
||||
public abstract T_Ciph createCipher(OmemoDevice contact);
|
||||
|
||||
/**
|
||||
* Get the id of the preKey used to establish the session.
|
||||
*
|
||||
* @return id
|
||||
*/
|
||||
public int getPreKeyId() {
|
||||
return this.preKeyId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt a message key for the recipient. This key can be deciphered by the recipient with its corresponding
|
||||
* session cipher. The key is then used to decipher the message.
|
||||
*
|
||||
* @param messageKey serialized key to encrypt
|
||||
* @return A CiphertextTuple containing the ciphertext and the messageType
|
||||
* @throws CryptoFailedException
|
||||
*/
|
||||
public abstract CiphertextTuple encryptMessageKey(byte[] messageKey) throws CryptoFailedException;
|
||||
|
||||
/**
|
||||
* Decrypt a messageKey using our sessionCipher. We can use that key to decipher the actual message.
|
||||
* Same as encryptMessageKey, just the other way round.
|
||||
*
|
||||
* @param encryptedKey encrypted key
|
||||
* @return serialized decrypted key or null
|
||||
* @throws CryptoFailedException when decryption fails.
|
||||
* @throws NoRawSessionException when no session was found in the double ratchet library
|
||||
*/
|
||||
public abstract byte[] decryptMessageKey(byte[] encryptedKey) throws CryptoFailedException, NoRawSessionException;
|
||||
|
||||
/**
|
||||
* Return the identityKey of the session.
|
||||
*
|
||||
* @return identityKey
|
||||
*/
|
||||
public T_IdKey getIdentityKey() {
|
||||
return identityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the identityKey of the remote device.
|
||||
* @param identityKey identityKey
|
||||
*/
|
||||
public void setIdentityKey(T_IdKey identityKey) {
|
||||
this.identityKey = identityKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the fingerprint of the contacts identityKey.
|
||||
*
|
||||
* @return fingerprint or null
|
||||
*/
|
||||
public OmemoFingerprint getFingerprint() {
|
||||
return (this.identityKey != null ? omemoStore.keyUtil().getFingerprint(this.identityKey) : null);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal.listener;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
import org.jivesoftware.smackx.carbons.packet.CarbonExtension;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
|
||||
/**
|
||||
* Internal listener for OMEMO encrypted carbon copies.
|
||||
*/
|
||||
public interface OmemoCarbonCopyStanzaReceivedListener {
|
||||
|
||||
void onOmemoCarbonCopyReceived(CarbonExtension.Direction direction, Message carbonCopy, Message wrappingMessage, OmemoManager.KnownBareJidGuard omemoManager);
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal.listener;
|
||||
|
||||
import org.jivesoftware.smack.packet.Stanza;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
|
||||
public interface OmemoMessageStanzaReceivedListener {
|
||||
|
||||
void onOmemoMessageStanzaReceived(Stanza stanza, OmemoManager.KnownBareJidGuard omemoManager);
|
||||
}
|
|
@ -14,23 +14,10 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.internal;
|
||||
|
||||
/**
|
||||
* Wrapper for IdentityKey objects.
|
||||
* StanzaListeners used for internal purposes.
|
||||
*
|
||||
* @author Paul Schaub
|
||||
* @see <a href="https://conversations.im/xeps/multi-end.html">XEP-0384: OMEMO</a>
|
||||
*/
|
||||
public class IdentityKeyWrapper {
|
||||
private final Object identityKey;
|
||||
|
||||
public IdentityKeyWrapper(Object wrapped) {
|
||||
identityKey = wrapped;
|
||||
}
|
||||
|
||||
public Object getIdentityKey() {
|
||||
return identityKey;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
package org.jivesoftware.smackx.omemo.internal.listener;
|
|
@ -0,0 +1,28 @@
|
|||
/**
|
||||
*
|
||||
* 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.trust;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
public interface TrustCallback {
|
||||
|
||||
TrustState getTrust(OmemoDevice device, OmemoFingerprint fingerprint);
|
||||
|
||||
void setTrust(OmemoDevice device, OmemoFingerprint fingerprint, TrustState state);
|
||||
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
/**
|
||||
*
|
||||
* 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.trust;
|
||||
|
||||
public enum TrustState {
|
||||
undecided, untrusted, trusted
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
/**
|
||||
* Callbacks used to pass trust decisions up to the client.
|
||||
*
|
||||
* @author Paul Schaub
|
||||
* @see <a href="https://conversations.im/xeps/multi-end.html">XEP-0384: OMEMO</a>
|
||||
*/
|
||||
package org.jivesoftware.smackx.omemo.trust;
|
|
@ -19,16 +19,14 @@ package org.jivesoftware.smackx.omemo.util;
|
|||
import java.io.IOException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoBundleVAxolotlElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoSession;
|
||||
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
|
@ -207,7 +205,7 @@ public abstract class OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @param count how many keys do we want to generate
|
||||
* @return Map of new preKeys
|
||||
*/
|
||||
public abstract HashMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count);
|
||||
public abstract TreeMap<Integer, T_PreKey> generateOmemoPreKeys(int startId, int count);
|
||||
|
||||
/**
|
||||
* Generate a new signed preKey.
|
||||
|
@ -330,7 +328,7 @@ public abstract class OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @param preKeyHashMap HashMap of preKeys
|
||||
* @return HashMap of byte arrays but with the same keyIds as key
|
||||
*/
|
||||
public HashMap<Integer, byte[]> preKeyPublisKeysForBundle(HashMap<Integer, T_PreKey> preKeyHashMap) {
|
||||
public HashMap<Integer, byte[]> preKeyPublisKeysForBundle(TreeMap<Integer, T_PreKey> preKeyHashMap) {
|
||||
HashMap<Integer, byte[]> out = new HashMap<>();
|
||||
for (Map.Entry<Integer, T_PreKey> e : preKeyHashMap.entrySet()) {
|
||||
out.put(e.getKey(), preKeyForBundle(e.getValue()));
|
||||
|
@ -352,32 +350,14 @@ public abstract class OmemoKeyUtil<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
|
|||
* @param identityKey identityKey
|
||||
* @return fingerprint of the key
|
||||
*/
|
||||
public abstract OmemoFingerprint getFingerprint(T_IdKey identityKey);
|
||||
public abstract OmemoFingerprint getFingerprintOfIdentityKey(T_IdKey identityKey);
|
||||
|
||||
/**
|
||||
* Create a new crypto-specific Session object.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param omemoStore omemoStore where we can save the session, get keys from etc.
|
||||
* @param from the device we want to create the session with.
|
||||
* @return a new session
|
||||
* Returns the fingerprint of the public key of an identityKeyPair.
|
||||
* @param identityKeyPair IdentityKeyPair.
|
||||
* @return fingerprint of the public key.
|
||||
*/
|
||||
public abstract OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
createOmemoSession(OmemoManager omemoManager, OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore,
|
||||
OmemoDevice from);
|
||||
|
||||
/**
|
||||
* Create a new concrete OmemoSession with a contact.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param omemoStore omemoStore
|
||||
* @param device device to establish the session with
|
||||
* @param identityKey identityKey of the device
|
||||
* @return concrete OmemoSession
|
||||
*/
|
||||
public abstract OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph>
|
||||
createOmemoSession(OmemoManager omemoManager, OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore,
|
||||
OmemoDevice device, T_IdKey identityKey);
|
||||
public abstract OmemoFingerprint getFingerprintOfIdentityKeyPair(T_IdKeyPair identityKeyPair);
|
||||
|
||||
/**
|
||||
* Deserialize a raw OMEMO Session from bytes.
|
||||
|
|
|
@ -28,7 +28,6 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.NoSuchProviderException;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.IllegalBlockSizeException;
|
||||
|
@ -38,17 +37,18 @@ import javax.crypto.SecretKey;
|
|||
import javax.crypto.spec.IvParameterSpec;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import org.jivesoftware.smack.SmackException;
|
||||
import org.jivesoftware.smack.util.StringUtils;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.OmemoManager;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.OmemoSessionManager;
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoVAxolotlElement;
|
||||
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.UndecidedOmemoIdentityException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoSession;
|
||||
|
||||
/**
|
||||
* Class used to build OMEMO messages.
|
||||
|
@ -65,8 +65,8 @@ import org.jivesoftware.smackx.omemo.internal.OmemoSession;
|
|||
* @author Paul Schaub
|
||||
*/
|
||||
public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||
private final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore;
|
||||
private final OmemoManager omemoManager;
|
||||
private final OmemoSessionManager<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> sessionManager;
|
||||
private final OmemoManager.KnownBareJidGuard managerGuard;
|
||||
|
||||
private byte[] messageKey = generateKey();
|
||||
private byte[] initializationVector = generateIv();
|
||||
|
@ -77,8 +77,8 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
/**
|
||||
* Create a OmemoMessageBuilder.
|
||||
*
|
||||
* @param omemoManager OmemoManager of our device.
|
||||
* @param omemoStore OmemoStore.
|
||||
* @param managerGuard OmemoManager of our device.
|
||||
* @param sessionManager OmemoSessionManager.
|
||||
* @param aesKey AES key that will be transported to the recipient. This is used eg. to encrypt the body.
|
||||
* @param iv IV
|
||||
* @throws NoSuchPaddingException
|
||||
|
@ -90,13 +90,13 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @throws NoSuchProviderException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
*/
|
||||
public OmemoMessageBuilder(OmemoManager omemoManager,
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore,
|
||||
public OmemoMessageBuilder(OmemoManager.KnownBareJidGuard managerGuard,
|
||||
OmemoSessionManager<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> sessionManager,
|
||||
byte[] aesKey, byte[] iv)
|
||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
this.omemoStore = omemoStore;
|
||||
this.omemoManager = omemoManager;
|
||||
this.managerGuard = managerGuard;
|
||||
this.sessionManager = sessionManager;
|
||||
this.messageKey = aesKey;
|
||||
this.initializationVector = iv;
|
||||
}
|
||||
|
@ -104,9 +104,9 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
/**
|
||||
* Create a new OmemoMessageBuilder with random IV and AES key.
|
||||
*
|
||||
* @param omemoManager omemoManager of our device.
|
||||
* @param omemoStore omemoStore.
|
||||
* @param message Messages body.
|
||||
* @param managerGuard omemoManager of our device.
|
||||
* @param sessionManager omemoSessionManager.
|
||||
* @param message Messages body.
|
||||
* @throws NoSuchPaddingException
|
||||
* @throws BadPaddingException
|
||||
* @throws InvalidKeyException
|
||||
|
@ -116,12 +116,13 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @throws NoSuchProviderException
|
||||
* @throws InvalidAlgorithmParameterException
|
||||
*/
|
||||
public OmemoMessageBuilder(OmemoManager omemoManager,
|
||||
OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> omemoStore, String message)
|
||||
public OmemoMessageBuilder(OmemoManager.KnownBareJidGuard managerGuard,
|
||||
OmemoSessionManager<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> sessionManager,
|
||||
String message)
|
||||
throws NoSuchPaddingException, BadPaddingException, InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException,
|
||||
UnsupportedEncodingException, NoSuchProviderException, InvalidAlgorithmParameterException {
|
||||
this.omemoManager = omemoManager;
|
||||
this.omemoStore = omemoStore;
|
||||
this.managerGuard = managerGuard;
|
||||
this.sessionManager = sessionManager;
|
||||
this.setMessage(message);
|
||||
}
|
||||
|
||||
|
@ -175,34 +176,40 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
* @throws UndecidedOmemoIdentityException
|
||||
* @throws CorruptedOmemoKeyException
|
||||
*/
|
||||
public void addRecipient(OmemoDevice device) throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException {
|
||||
public void addRecipient(OmemoDevice device)
|
||||
throws CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
||||
CannotEstablishOmemoSessionException {
|
||||
addRecipient(device, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new recipient device to the message.
|
||||
* @param device recipient device
|
||||
* @param contactsDevice recipient device
|
||||
* @param ignoreTrust ignore current trust state? Useful for keyTransportMessages that are sent to repair a session
|
||||
* @throws CryptoFailedException
|
||||
* @throws UndecidedOmemoIdentityException
|
||||
* @throws CorruptedOmemoKeyException
|
||||
*/
|
||||
public void addRecipient(OmemoDevice device, boolean ignoreTrust) throws
|
||||
CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException {
|
||||
OmemoSession<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> session =
|
||||
omemoStore.getOmemoSessionOf(omemoManager, device);
|
||||
public void addRecipient(OmemoDevice contactsDevice, boolean ignoreTrust) throws
|
||||
CryptoFailedException, UndecidedOmemoIdentityException, CorruptedOmemoKeyException,
|
||||
CannotEstablishOmemoSessionException {
|
||||
|
||||
if (session != null) {
|
||||
if (!ignoreTrust && !omemoStore.isDecidedOmemoIdentity(omemoManager, device, session.getIdentityKey())) {
|
||||
// Warn user of undecided device
|
||||
throw new UndecidedOmemoIdentityException(device);
|
||||
}
|
||||
OmemoFingerprint fingerprint;
|
||||
try {
|
||||
fingerprint = managerGuard.get().getFingerprint(contactsDevice);
|
||||
} catch (SmackException.NotLoggedInException e) {
|
||||
throw new AssertionError("This should never happen.");
|
||||
}
|
||||
|
||||
if (!ignoreTrust && omemoStore.isTrustedOmemoIdentity(omemoManager, device, session.getIdentityKey())) {
|
||||
// Encrypt key and save to header
|
||||
CiphertextTuple encryptedKey = session.encryptMessageKey(messageKey);
|
||||
keys.add(new OmemoVAxolotlElement.OmemoHeader.Key(encryptedKey.getCiphertext(), device.getDeviceId(), encryptedKey.isPreKeyMessage()));
|
||||
}
|
||||
if (!ignoreTrust && !managerGuard.get().isDecidedOmemoIdentity(contactsDevice, fingerprint)) {
|
||||
// Warn user of undecided device
|
||||
throw new UndecidedOmemoIdentityException(contactsDevice);
|
||||
}
|
||||
|
||||
if (ignoreTrust || managerGuard.get().isTrustedOmemoIdentity(contactsDevice, fingerprint)) {
|
||||
// Encrypt key and save to header
|
||||
CiphertextTuple encryptedKey = sessionManager.doubleRatchetEncrypt(contactsDevice, messageKey);
|
||||
keys.add(new OmemoVAxolotlElement.OmemoHeader.Key(encryptedKey.getCiphertext(), contactsDevice.getDeviceId(), encryptedKey.isPreKeyMessage()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -213,7 +220,7 @@ public class OmemoMessageBuilder<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
|
|||
*/
|
||||
public OmemoVAxolotlElement finish() {
|
||||
OmemoVAxolotlElement.OmemoHeader header = new OmemoVAxolotlElement.OmemoHeader(
|
||||
omemoManager.getDeviceId(),
|
||||
managerGuard.get().getDeviceId(),
|
||||
keys,
|
||||
initializationVector
|
||||
);
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
|
@ -67,5 +68,11 @@ public class DeviceListTest {
|
|||
!cached.getInactiveDevices().contains(4));
|
||||
|
||||
assertTrue(cached.getAllDevices().size() == 4);
|
||||
|
||||
assertFalse(cached.contains(17));
|
||||
cached.addDevice(17);
|
||||
assertTrue(cached.getActiveDevices().contains(17));
|
||||
|
||||
assertNotNull(cached.toString());
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,43 @@
|
|||
/**
|
||||
*
|
||||
* Copyright 2017 Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
public class OmemoMessageInformationTest {
|
||||
|
||||
@Test
|
||||
public void setterGetterTest() throws XmppStringprepException {
|
||||
OmemoMessageInformation information = new OmemoMessageInformation();
|
||||
assertEquals(information.getCarbon(), OmemoMessageInformation.CARBON.NONE);
|
||||
information.setCarbon(OmemoMessageInformation.CARBON.RECV);
|
||||
assertEquals(OmemoMessageInformation.CARBON.RECV, information.getCarbon());
|
||||
|
||||
assertNull(information.getSenderDevice());
|
||||
OmemoDevice device = new OmemoDevice(JidCreate.bareFrom("test@a.bc"), 14);
|
||||
information.setSenderDevice(device);
|
||||
assertEquals(device, information.getSenderDevice());
|
||||
}
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/**
|
||||
*
|
||||
* Copyright Paul Schaub
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo;
|
||||
|
||||
import static junit.framework.TestCase.assertEquals;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static junit.framework.TestCase.assertNotNull;
|
||||
import static junit.framework.TestCase.assertNotSame;
|
||||
import static junit.framework.TestCase.assertNull;
|
||||
import static junit.framework.TestCase.assertSame;
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.FileBasedOmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.OmemoStore;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CorruptedOmemoKeyException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CachedDeviceList;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
|
||||
import org.junit.AfterClass;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.jxmpp.jid.impl.JidCreate;
|
||||
import org.jxmpp.stringprep.XmppStringprepException;
|
||||
|
||||
public abstract class OmemoStoreTest<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> {
|
||||
|
||||
protected final OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store;
|
||||
private final OmemoDevice alice, bob;
|
||||
|
||||
private static final TemporaryFolder tmp = initStaticTemp();
|
||||
|
||||
OmemoStoreTest(OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Sess, T_Addr, T_ECPub, T_Bundle, T_Ciph> store)
|
||||
throws XmppStringprepException {
|
||||
this.store = store;
|
||||
alice = new OmemoDevice(JidCreate.bareFrom("alice@wonderland.lit"), 123);
|
||||
bob = new OmemoDevice(JidCreate.bareFrom("bob@builder.tv"), 987);
|
||||
}
|
||||
|
||||
// Tests
|
||||
|
||||
@Test
|
||||
public void keyUtilNotNull() {
|
||||
assertNotNull(store.keyUtil());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateOmemoIdentityKeyPairDoesNotReturnNull() {
|
||||
assertNotNull(store.generateOmemoIdentityKeyPair());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identityKeyFromIdentityKeyPairIsNotNull() {
|
||||
T_IdKeyPair pair = store.generateOmemoIdentityKeyPair();
|
||||
assertNotNull(store.keyUtil().identityKeyFromPair(pair));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeLoadRemoveOmemoIdentityKeyPair()
|
||||
throws IOException, CorruptedOmemoKeyException
|
||||
{
|
||||
T_IdKeyPair before = store.generateOmemoIdentityKeyPair();
|
||||
|
||||
assertNull(store.loadOmemoIdentityKeyPair(alice));
|
||||
store.storeOmemoIdentityKeyPair(alice, before);
|
||||
|
||||
T_IdKeyPair after = store.loadOmemoIdentityKeyPair(alice);
|
||||
assertNotNull(after);
|
||||
|
||||
// Fingerprints equal
|
||||
assertEquals(store.keyUtil().getFingerprintOfIdentityKeyPair(before),
|
||||
store.keyUtil().getFingerprintOfIdentityKeyPair(after));
|
||||
|
||||
// Byte-representation equals
|
||||
assertTrue(Arrays.equals(
|
||||
store.keyUtil().identityKeyPairToBytes(before),
|
||||
store.keyUtil().identityKeyPairToBytes(after)));
|
||||
|
||||
// Non-existing keypair
|
||||
assertNull("Must return null for non-existing key pairs.", store.loadOmemoIdentityKeyPair(bob));
|
||||
|
||||
// Deleting works
|
||||
store.removeOmemoIdentityKeyPair(alice);
|
||||
assertNull(store.loadOmemoIdentityKeyPair(alice));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeLoadRemoveOmemoIdentityKey()
|
||||
throws IOException, CorruptedOmemoKeyException
|
||||
{
|
||||
// Create IdentityKeys and get bytes
|
||||
T_IdKey keyA1 = store.keyUtil().identityKeyFromPair(store.generateOmemoIdentityKeyPair());
|
||||
T_IdKey keyB1 = store.keyUtil().identityKeyFromPair(store.generateOmemoIdentityKeyPair());
|
||||
byte[] bytesA1 = store.keyUtil().identityKeyToBytes(keyA1);
|
||||
byte[] bytesB = store.keyUtil().identityKeyToBytes(keyB1);
|
||||
|
||||
// Not null and not of length 0
|
||||
assertNotNull("Serialized identityKey cannot be null.", bytesA1);
|
||||
assertNotNull("Serialized identityKey cannot be null.", bytesB);
|
||||
assertNotSame("Serialized identityKey must be of length > 0.", 0, bytesA1.length);
|
||||
assertNotSame("Serialized identityKey must be of length > 0.", 0, bytesB.length);
|
||||
|
||||
// Keys do not equal
|
||||
assertFalse("Generated IdentityKeys must not be equal (ULTRA unlikely).",
|
||||
Arrays.equals(bytesA1, bytesB));
|
||||
|
||||
// Loading must return null before and not null after saving
|
||||
assertNull("Must return null, the store could not have this key by now.",
|
||||
store.loadOmemoIdentityKey(alice, bob));
|
||||
store.storeOmemoIdentityKey(alice, bob, keyA1);
|
||||
T_IdKey keyA2 = store.loadOmemoIdentityKey(alice, bob);
|
||||
assertNotNull(keyA2);
|
||||
|
||||
// Loaded key must equal stored one
|
||||
byte[] bytesA2 = store.keyUtil().identityKeyToBytes(keyA2);
|
||||
assertTrue("Serialized loaded key must equal serialized stored one.",
|
||||
Arrays.equals(bytesA1, bytesA2));
|
||||
|
||||
// Non-existing keys must return null
|
||||
assertNull("Non-existing keys must be returned as null.", store.loadOmemoIdentityKey(bob, alice));
|
||||
|
||||
// Key must vanish when deleted.
|
||||
store.removeOmemoIdentityKey(alice, bob);
|
||||
assertNull(store.loadOmemoIdentityKey(alice, bob));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generateOmemoPreKeys() {
|
||||
TreeMap<Integer, T_PreKey> keys = store.generateOmemoPreKeys(31, 49);
|
||||
assertNotNull("Generated data structure must not be null.", keys);
|
||||
|
||||
byte[] lastKey = null;
|
||||
|
||||
for (int i = 31; i <= 79; i++) {
|
||||
assertEquals("Key ids must be ascending order, starting at 31.", Integer.valueOf(i), keys.firstKey());
|
||||
assertNotNull("Every id must match to a key.", keys.get(keys.firstKey()));
|
||||
byte[] bytes = store.keyUtil().preKeyToBytes(keys.get(keys.firstKey()));
|
||||
assertNotNull("Serialized preKey must not be null.", bytes);
|
||||
assertNotSame("Serialized preKey must not be of length 0.", 0, bytes.length);
|
||||
|
||||
if (lastKey != null) {
|
||||
assertFalse("PreKeys MUST NOT be equal.", Arrays.equals(lastKey, bytes));
|
||||
}
|
||||
lastKey = bytes;
|
||||
|
||||
keys.remove(keys.firstKey());
|
||||
|
||||
}
|
||||
|
||||
assertEquals("After deleting 49 keys, there must be no keys left.", 0, keys.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeLoadRemoveOmemoPreKeys()
|
||||
throws IOException, InterruptedException
|
||||
{
|
||||
TreeMap<Integer, T_PreKey> before = store.generateOmemoPreKeys(1, 10);
|
||||
assertEquals("The store must have no prekeys before this test.", 0, store.loadOmemoPreKeys(alice).size());
|
||||
|
||||
store.storeOmemoPreKeys(alice, before);
|
||||
TreeMap<Integer, T_PreKey> after = store.loadOmemoPreKeys(alice);
|
||||
assertNotNull("Loaded preKeys must not be null.", after);
|
||||
assertEquals("Loaded preKey count must equal stored count.", before.size(), after.size());
|
||||
|
||||
// Non-existing key must be returned as null
|
||||
assertNull("Non-existing preKey must be returned as null.", store.loadOmemoPreKey(alice, 10000));
|
||||
|
||||
int last = after.size();
|
||||
for (int i = 1; i <= last; i++) {
|
||||
T_PreKey bKey = before.get(i);
|
||||
T_PreKey aKey = after.get(i);
|
||||
|
||||
assertTrue("Loaded keys must equal stored ones.", Arrays.equals(
|
||||
store.keyUtil().preKeyToBytes(bKey),
|
||||
store.keyUtil().preKeyToBytes(aKey)));
|
||||
|
||||
T_PreKey rKey = store.loadOmemoPreKey(alice, i);
|
||||
assertNotNull("Randomly accessed preKeys must not be null.", rKey);
|
||||
assertTrue("Randomly accessed preKeys must equal the stored ones.", Arrays.equals(
|
||||
store.keyUtil().preKeyToBytes(aKey),
|
||||
store.keyUtil().preKeyToBytes(rKey)));
|
||||
|
||||
store.removeOmemoPreKey(alice, i);
|
||||
assertNull("PreKey must be null after deletion.", store.loadOmemoPreKey(alice, i));
|
||||
}
|
||||
|
||||
TreeMap<Integer, T_PreKey> postDeletion = store.loadOmemoPreKeys(alice);
|
||||
assertSame("PreKey count must equal 0 after deletion of all keys.", 0, postDeletion.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void storeLoadRemoveOmemoSignedPreKeys()
|
||||
throws IOException, CorruptedOmemoKeyException
|
||||
{
|
||||
TreeMap<Integer, T_SigPreKey> before = store.loadOmemoSignedPreKeys(alice);
|
||||
assertEquals("At this stage, there must be no signed prekeys in the store.", 0, before.size());
|
||||
|
||||
T_IdKeyPair idp = store.generateOmemoIdentityKeyPair();
|
||||
T_SigPreKey spk = store.generateOmemoSignedPreKey(idp, 125);
|
||||
|
||||
assertNotNull("SignedPreKey must not be null.", spk);
|
||||
assertEquals("ID of signedPreKey must match.", 125, store.keyUtil().signedPreKeyIdFromKey(spk));
|
||||
byte[] bytes = store.keyUtil().signedPreKeyToBytes(spk);
|
||||
assertNotNull("Serialized signedPreKey must not be null", bytes);
|
||||
assertNotSame("Serialized signedPreKey must not be of length 0.", 0, bytes.length);
|
||||
|
||||
// Stored key must equal loaded key
|
||||
store.storeOmemoSignedPreKey(alice, 125, spk);
|
||||
TreeMap<Integer, T_SigPreKey> after = store.loadOmemoSignedPreKeys(alice);
|
||||
assertEquals("We must have exactly 1 signedPreKey now.", 1, after.size());
|
||||
T_SigPreKey spk2 = after.get(after.firstKey());
|
||||
assertEquals("Id of the stored signedPreKey must match the one we stored.",
|
||||
125, store.keyUtil().signedPreKeyIdFromKey(spk2));
|
||||
assertTrue("Serialization of stored and loaded signed preKey must equal.", Arrays.equals(
|
||||
store.keyUtil().signedPreKeyToBytes(spk), store.keyUtil().signedPreKeyToBytes(spk2)));
|
||||
|
||||
// Random access
|
||||
T_SigPreKey rspk = store.loadOmemoSignedPreKey(alice, 125);
|
||||
assertTrue("Serialization of stored and randomly accessed signed preKey must equal.", Arrays.equals(
|
||||
store.keyUtil().signedPreKeyToBytes(spk), store.keyUtil().signedPreKeyToBytes(rspk)));
|
||||
assertNull("Non-existing signedPreKey must be returned as null.",
|
||||
store.loadOmemoSignedPreKey(alice, 10000));
|
||||
|
||||
// Deleting
|
||||
store.removeOmemoSignedPreKey(alice, 125);
|
||||
assertNull("Deleted key must be returned as null.", store.loadOmemoSignedPreKey(alice, 125));
|
||||
assertEquals(0, store.loadOmemoSignedPreKeys(alice).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadStoreDateOfLastSignedPreKeyRenewal() throws IOException {
|
||||
assertNull("The date of last signed preKey renewal must be null at this stage.",
|
||||
store.getDateOfLastSignedPreKeyRenewal(alice));
|
||||
Date before = new Date();
|
||||
store.setDateOfLastSignedPreKeyRenewal(alice, before);
|
||||
Date after = store.getDateOfLastSignedPreKeyRenewal(alice);
|
||||
assertEquals("Dates must equal.", after, before);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadStoreDateOfLastMessageReceived() throws IOException {
|
||||
assertNull("The date of last message received must be null at this stage.",
|
||||
store.getDateOfLastReceivedMessage(alice, bob));
|
||||
Date before = new Date();
|
||||
store.setDateOfLastReceivedMessage(alice, bob, before);
|
||||
Date after = store.getDateOfLastReceivedMessage(alice, bob);
|
||||
assertEquals("Dates must equal.", after, before);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadStoreCachedDeviceList() throws IOException {
|
||||
Integer[] active = new Integer[] {1,5,999,10};
|
||||
Integer[] inactive = new Integer[] {6,7,8};
|
||||
CachedDeviceList before = new CachedDeviceList(
|
||||
new HashSet<>(Arrays.asList(active)),
|
||||
new HashSet<>(Arrays.asList(inactive)));
|
||||
|
||||
assertNotNull("Loading a non-existent cached deviceList must return an empty list.",
|
||||
store.loadCachedDeviceList(alice, bob.getJid()));
|
||||
|
||||
store.storeCachedDeviceList(alice, bob.getJid(), before);
|
||||
CachedDeviceList after = store.loadCachedDeviceList(alice, bob.getJid());
|
||||
assertTrue("Loaded deviceList must not be empty", after.getAllDevices().size() != 0);
|
||||
|
||||
assertEquals("Number of entries in active devices must match.", active.length, after.getActiveDevices().size());
|
||||
assertEquals("Number of entries in inactive devices must match.", inactive.length, after.getInactiveDevices().size());
|
||||
assertEquals("Number of total entries must match.", active.length + inactive.length, after.getAllDevices().size());
|
||||
|
||||
for (Integer a : active) {
|
||||
assertTrue(after.getActiveDevices().contains(a));
|
||||
assertTrue(after.getAllDevices().contains(a));
|
||||
}
|
||||
|
||||
for (Integer i : inactive) {
|
||||
assertTrue(after.getInactiveDevices().contains(i));
|
||||
assertTrue(after.getAllDevices().contains(i));
|
||||
}
|
||||
|
||||
store.storeCachedDeviceList(alice, bob.getJid(), new CachedDeviceList());
|
||||
assertEquals("DeviceList must be empty after overwriting it with empty list.", 0,
|
||||
store.loadCachedDeviceList(alice, bob.getJid()).getAllDevices().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadAllRawSessionsReturnsEmptyMapTest() {
|
||||
HashMap<Integer, T_Sess> sessions = store.loadAllRawSessionsOf(alice, bob.getJid());
|
||||
assertNotNull(sessions);
|
||||
assertEquals(0, sessions.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadNonExistentRawSessionReturnsNullTest() {
|
||||
T_Sess session = store.loadRawSession(alice, bob);
|
||||
assertNull(session);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void getFingerprint() throws IOException, CorruptedOmemoKeyException {
|
||||
assertNull("Method must return null for a non-existent fingerprint.", store.getFingerprint(alice));
|
||||
store.storeOmemoIdentityKeyPair(alice, store.generateOmemoIdentityKeyPair());
|
||||
OmemoFingerprint fingerprint = store.getFingerprint(alice);
|
||||
assertNotNull("fingerprint must not be null", fingerprint);
|
||||
assertEquals("Fingerprint must be of length 64", 64, fingerprint.length());
|
||||
|
||||
store.removeOmemoIdentityKeyPair(alice); //clean up
|
||||
}
|
||||
|
||||
// ##############################################################
|
||||
// Workaround for https://github.com/junit-team/junit4/issues/671
|
||||
|
||||
static TemporaryFolder initStaticTemp() {
|
||||
try {
|
||||
return new TemporaryFolder() { { before(); } };
|
||||
} catch (Throwable t) {
|
||||
throw new RuntimeException(t);
|
||||
}
|
||||
}
|
||||
|
||||
@AfterClass
|
||||
public static void cleanup() throws Exception {
|
||||
FileBasedOmemoStore.deleteDirectory(tmp.getRoot());
|
||||
}
|
||||
|
||||
// ##############################################################
|
||||
}
|
|
@ -26,13 +26,11 @@ import java.security.NoSuchAlgorithmException;
|
|||
import java.security.Security;
|
||||
|
||||
import org.jivesoftware.smack.packet.Message;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.element.OmemoElement;
|
||||
import org.jivesoftware.smackx.omemo.exceptions.CryptoFailedException;
|
||||
import org.jivesoftware.smackx.omemo.internal.CipherAndAuthTag;
|
||||
import org.jivesoftware.smackx.omemo.internal.CiphertextTuple;
|
||||
import org.jivesoftware.smackx.omemo.internal.ClearTextMessage;
|
||||
import org.jivesoftware.smackx.omemo.internal.IdentityKeyWrapper;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoMessageInformation;
|
||||
import org.jivesoftware.smackx.omemo.util.OmemoMessageBuilder;
|
||||
|
@ -47,13 +45,6 @@ import org.jxmpp.jid.impl.JidCreate;
|
|||
*/
|
||||
public class WrapperObjectsTest {
|
||||
|
||||
@Test
|
||||
public void identityKeyWrapperTest() {
|
||||
Object pseudoKey = new Object();
|
||||
IdentityKeyWrapper wrapper = new IdentityKeyWrapper(pseudoKey);
|
||||
assertEquals(pseudoKey, wrapper.getIdentityKey());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ciphertextTupleTest() {
|
||||
byte[] c = OmemoMessageBuilder.generateIv();
|
||||
|
@ -69,17 +60,14 @@ public class WrapperObjectsTest {
|
|||
|
||||
@Test
|
||||
public void clearTextMessageTest() throws Exception {
|
||||
Object pseudoKey = new Object();
|
||||
IdentityKeyWrapper wrapper = new IdentityKeyWrapper(pseudoKey);
|
||||
BareJid senderJid = JidCreate.bareFrom("bob@server.tld");
|
||||
OmemoDevice sender = new OmemoDevice(senderJid, 1234);
|
||||
OmemoMessageInformation information = new OmemoMessageInformation(wrapper, sender, OmemoMessageInformation.CARBON.NONE);
|
||||
OmemoMessageInformation information = new OmemoMessageInformation(null, sender, OmemoMessageInformation.CARBON.NONE);
|
||||
|
||||
assertTrue("OmemoInformation must state that the message is an OMEMO message.",
|
||||
information.isOmemoMessage());
|
||||
assertEquals(OmemoMessageInformation.CARBON.NONE, information.getCarbon());
|
||||
assertEquals(sender, information.getSenderDevice());
|
||||
assertEquals(wrapper, information.getSenderIdentityKey());
|
||||
|
||||
String body = "Decrypted Body";
|
||||
Message message = new Message(senderJid, body);
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
*
|
||||
* Copyright the original author or authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.jivesoftware.smack.omemo.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
import org.jivesoftware.smackx.omemo.OmemoFingerprint;
|
||||
import org.jivesoftware.smackx.omemo.internal.OmemoDevice;
|
||||
import org.jivesoftware.smackx.omemo.trust.TrustCallback;
|
||||
import org.jivesoftware.smackx.omemo.trust.TrustState;
|
||||
|
||||
/**
|
||||
* Ephemera Trust Callback used to make trust decisions in tests.
|
||||
*/
|
||||
public class EphemeralTrustCallback implements TrustCallback {
|
||||
|
||||
private final HashMap<OmemoDevice, HashMap<OmemoFingerprint, TrustState>> trustStates = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public TrustState getTrust(OmemoDevice device, OmemoFingerprint fingerprint) {
|
||||
HashMap<OmemoFingerprint, TrustState> states = trustStates.get(device);
|
||||
|
||||
if (states != null) {
|
||||
TrustState state = states.get(fingerprint);
|
||||
|
||||
if (state != null) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
return TrustState.undecided;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrust(OmemoDevice device, OmemoFingerprint fingerprint, TrustState state) {
|
||||
HashMap<OmemoFingerprint, TrustState> states = trustStates.get(device);
|
||||
|
||||
if (states == null) {
|
||||
states = new HashMap<>();
|
||||
trustStates.put(device, states);
|
||||
}
|
||||
|
||||
states.put(fingerprint, state);
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue