Stale devices: Consider date of deviceId publication

This commit is contained in:
Paul Schaub 2018-01-29 18:37:04 +01:00
parent 38d9d96d1f
commit 15083fa3b0
6 changed files with 160 additions and 22 deletions

View File

@ -165,6 +165,28 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
return last;
}
@Override
public void setDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
getCache(userDevice).lastDeviceIdPublicationDates.put(contactsDevice, date);
if (persistent != null) {
persistent.setDateOfLastReceivedMessage(userDevice, contactsDevice, date);
}
}
@Override
public Date getDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice) {
Date last = getCache(userDevice).lastDeviceIdPublicationDates.get(contactsDevice);
if (last == null && persistent != null) {
last = persistent.getDateOfLastDeviceIdPublication(userDevice, contactsDevice);
if (last != null) {
getCache(userDevice).lastDeviceIdPublicationDates.put(contactsDevice, last);
}
}
return last;
}
@Override
public void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date) {
getCache(userDevice).lastRenewalDate = date;
@ -420,6 +442,7 @@ public class CachingOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_Se
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<OmemoDevice, Date> lastDeviceIdPublicationDates = new HashMap<>();
private final HashMap<BareJid, OmemoCachedDeviceList> deviceLists = new HashMap<>();
private Date lastRenewalDate = null;
}

View File

@ -105,12 +105,6 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
}
}
@Override
public void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(userDevice, contactsDevice);
writeLong(lastMessageReceived, date.getTime());
}
@Override
public SortedSet<Integer> localDeviceIdsOf(BareJid localUser) {
SortedSet<Integer> deviceIds = new TreeSet<>();
@ -128,6 +122,12 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
return deviceIds;
}
@Override
public void setDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(userDevice, contactsDevice);
writeLong(lastMessageReceived, date.getTime());
}
@Override
public Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File lastMessageReceived = hierarchy.getLastMessageReceivedDatePath(userDevice, contactsDevice);
@ -135,6 +135,19 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
return date != null ? new Date(date) : null;
}
@Override
public void setDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date) {
File lastDeviceIdPublished = hierarchy.getLastDeviceIdPublicationDatePath(userDevice, contactsDevice);
writeLong(lastDeviceIdPublished, date.getTime());
}
@Override
public Date getDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice) {
File lastDeviceIdPublished = hierarchy.getLastDeviceIdPublicationDatePath(userDevice, contactsDevice);
Long date = readLong(lastDeviceIdPublished);
return date != null ? new Date(date) : null;
}
@Override
public void setDateOfLastSignedPreKeyRenewal(OmemoDevice userDevice, Date date) {
File lastSignedPreKeyRenewal = hierarchy.getLastSignedPreKeyRenewal(userDevice);
@ -753,6 +766,7 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
static final String IDENTITY_KEY_PAIR = "identityKeyPair";
static final String PRE_KEYS = "preKeys";
static final String LAST_MESSAGE_RECEVIED_DATE = "lastMessageReceivedDate";
static final String LAST_DEVICEID_PUBLICATION_DATE = "lastDeviceIdPublicationDate";
static final String SIGNED_PRE_KEYS = "signedPreKeys";
static final String LAST_SIGNED_PRE_KEY_RENEWAL = "lastSignedPreKeyRenewal";
static final String SESSION = "session";
@ -812,6 +826,10 @@ public abstract class FileBasedOmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigP
return new File(getContactsDir(userDevice, device), LAST_MESSAGE_RECEVIED_DATE);
}
File getLastDeviceIdPublicationDatePath(OmemoDevice userDevice, OmemoDevice device) {
return new File(getContactsDir(userDevice, device), LAST_DEVICEID_PUBLICATION_DATE);
}
File getSignedPreKeysDirectory(OmemoDevice userDevice) {
return createDirectory(getUserDeviceDirectory(userDevice), SIGNED_PRE_KEYS);
}

View File

@ -22,6 +22,7 @@ import static org.jivesoftware.smackx.omemo.util.OmemoConstants.PEP_NODE_DEVICE_
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@ -245,6 +246,10 @@ public final class OmemoManager extends Manager {
throw new SmackException.NotLoggedInException();
}
if (getTrustCallback() == null) {
throw new IllegalStateException("No TrustCallback set.");
}
getOmemoService().init(new LoggedInOmemoManager(this));
ServiceDiscoveryManager.getInstanceFor(connection()).addFeature(PEP_NODE_DEVICE_LIST_NOTIFY);
}
@ -1061,6 +1066,9 @@ public final class OmemoManager extends Manager {
final OmemoDeviceListElement_VAxolotl newDeviceList = new OmemoDeviceListElement_VAxolotl(deviceList);
if (!newDeviceList.copyDeviceIds().equals(receivedDeviceList.copyDeviceIds())) {
LOGGER.log(Level.FINE, "Republish deviceList due to changes:" +
" Received: " + Arrays.toString(receivedDeviceList.copyDeviceIds().toArray()) +
" Published: " + Arrays.toString(newDeviceList.copyDeviceIds().toArray()));
Async.go(new Runnable() {
@Override
public void run() {

View File

@ -386,16 +386,25 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
// Ignore stale devices
if (OmemoConfiguration.getIgnoreStaleDevices()) {
Date lastActivity = getOmemoStoreBackend().getDateOfLastReceivedMessage(userDevice, contactsDevice);
if (lastActivity == null) {
lastActivity = new Date();
getOmemoStoreBackend().setDateOfLastReceivedMessage(userDevice, contactsDevice, lastActivity);
Date lastMessageDate = getOmemoStoreBackend().getDateOfLastReceivedMessage(userDevice, contactsDevice);
if (lastMessageDate == null) {
lastMessageDate = new Date();
getOmemoStoreBackend().setDateOfLastReceivedMessage(userDevice, contactsDevice, lastMessageDate);
}
if (isStale(userDevice, contactsDevice, lastActivity, OmemoConfiguration.getIgnoreStaleDevicesAfterHours())) {
Date lastPublicationDate = getOmemoStoreBackend().getDateOfLastDeviceIdPublication(userDevice, contactsDevice);
if (lastPublicationDate == null) {
lastPublicationDate = new Date();
getOmemoStoreBackend().setDateOfLastDeviceIdPublication(userDevice, contactsDevice, lastPublicationDate);
}
boolean stale = isStale(userDevice, contactsDevice, lastPublicationDate, OmemoConfiguration.getIgnoreStaleDevicesAfterHours());
stale &= isStale(userDevice, contactsDevice, lastMessageDate, OmemoConfiguration.getIgnoreStaleDevicesAfterHours());
if (stale) {
LOGGER.log(Level.FINE, "Device " + contactsDevice + " seems to be stale (last message received "
+ lastActivity + "). Ignore it.");
skippedRecipients.put(contactsDevice, new StaleDeviceException(contactsDevice, lastActivity));
+ lastMessageDate + ", last publication of deviceId: " + lastPublicationDate + "). Ignore it.");
skippedRecipients.put(contactsDevice, new StaleDeviceException(contactsDevice, lastMessageDate, lastPublicationDate));
continue;
}
}
@ -860,6 +869,15 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
return devicesWithSessions;
}
/**
* Return a set of all devices from the provided set, which trust level is undecided.
* A device is also considered undecided, if its fingerprint cannot be loaded.
*
* @param userDevice our OmemoDevice
* @param callback OmemoTrustCallback to query the trust decisions from
* @param devices set of OmemoDevices
* @return set of OmemoDevices which contains all devices from the set devices, which are undecided
*/
private Set<OmemoDevice> getUndecidedDevices(OmemoDevice userDevice, OmemoTrustCallback callback, Set<OmemoDevice> devices) {
Set<OmemoDevice> undecidedDevices = new HashSet<>();
@ -883,6 +901,15 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
return undecidedDevices;
}
/**
* Return a set of all devices from the provided set, which are untrusted.
* A device is also considered untrusted, if its fingerprint cannot be loaded.
*
* @param userDevice our own OmemoDevice
* @param trustCallback OmemoTrustCallback to query trust decisions from
* @param devices set of OmemoDevices
* @return set of OmemoDevices from devices, which contains all devices which are untrusted
*/
private Set<OmemoDevice> getUntrustedDeviced(OmemoDevice userDevice, OmemoTrustCallback trustCallback, Set<OmemoDevice> devices) {
Set<OmemoDevice> untrustedDevices = new HashSet<>();
@ -1009,13 +1036,22 @@ public abstract class OmemoService<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey,
for (int deviceId : deviceList.getActiveDevices()) {
OmemoDevice device = new OmemoDevice(contact, deviceId);
Date lastDeviceIdPublication = getOmemoStoreBackend().getDateOfLastDeviceIdPublication(userDevice, device);
if (lastDeviceIdPublication == null) {
lastDeviceIdPublication = new Date();
getOmemoStoreBackend().setDateOfLastDeviceIdPublication(userDevice, device, lastDeviceIdPublication);
}
Date lastMessageReceived = getOmemoStoreBackend().getDateOfLastReceivedMessage(userDevice, device);
if (lastMessageReceived == null) {
lastMessageReceived = new Date();
getOmemoStoreBackend().setDateOfLastReceivedMessage(userDevice, device, lastMessageReceived);
}
if (isStale(userDevice, device, lastMessageReceived, maxAgeHours)) {
boolean stale = isStale(userDevice, device, lastDeviceIdPublication, maxAgeHours);
stale &= isStale(userDevice, device, lastMessageReceived, maxAgeHours);
if (stale) {
deviceList.addInactiveDevice(deviceId);
}
}

View File

@ -108,11 +108,19 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
cached = new OmemoCachedDeviceList();
}
if (list != null) {
cached.merge(list.getDeviceIds());
storeCachedDeviceList(userDevice, contact, cached);
if (list == null) {
return cached;
}
for (int devId : list.getDeviceIds()) {
if (!cached.contains(devId)) {
setDateOfLastDeviceIdPublication(userDevice, new OmemoDevice(contact, devId), new Date());
}
}
cached.merge(list.getDeviceIds());
storeCachedDeviceList(userDevice, contact, cached);
return cached;
}
@ -300,6 +308,26 @@ public abstract class OmemoStore<T_IdKeyPair, T_IdKey, T_PreKey, T_SigPreKey, T_
*/
public abstract Date getDateOfLastReceivedMessage(OmemoDevice userDevice, OmemoDevice contactsDevice);
/**
* Set the date of the last time the deviceId was published. This method only gets called, when the deviceId
* was inactive/non-existent before it was published.
*
* @param userDevice our OmemoDevice
* @param contactsDevice OmemoDevice in question
* @param date date of the last publication after not being published
*/
public abstract void setDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice, Date date);
/**
* Return the date of the last time the deviceId was published after previously being not published.
* (Point in time, where the status of the deviceId changed from inactive/non-existent to active).
*
* @param userDevice our OmemoDevice
* @param contactsDevice OmemoDevice in question
* @return date of the last publication after not being published
*/
public abstract Date getDateOfLastDeviceIdPublication(OmemoDevice userDevice, OmemoDevice contactsDevice);
/**
* Set the date of the last time the signed preKey was renewed.
*

View File

@ -24,17 +24,42 @@ public class StaleDeviceException extends Exception {
private static final long serialVersionUID = 1L;
private final OmemoDevice device;
private final Date lastActivity;
private final Date lastMessageDate;
private final Date lastDeviceIdPublication;
public StaleDeviceException(OmemoDevice device, Date lastActivity) {
/**
* This exception gets thrown if a message cannot be encrypted for a device due to the device being inactive for too long (stale).
*
* @param device OmemoDevice.
* @param lastMessageDate
* @param lastDeviceIdPublicationDate
*/
public StaleDeviceException(OmemoDevice device, Date lastMessageDate, Date lastDeviceIdPublicationDate) {
this.device = device;
this.lastActivity = lastActivity;
this.lastMessageDate = lastMessageDate;
this.lastDeviceIdPublication = lastDeviceIdPublicationDate;
}
public Date getLastActivity() {
return lastActivity;
/**
* Return the date on which the last OMEMO message sent from the device was received.
* @return last messages date
*/
public Date getLastMessageDate() {
return lastMessageDate;
}
/**
* Return the date of the last time the deviceId was republished after being inactive/non-existent before.
* @return date of last deviceId (re)publication.
*/
public Date getLastDeviceIdPublicationDate() {
return lastDeviceIdPublication;
}
/**
* Return the stale OMEMO device.
* @return stale device
*/
public OmemoDevice getDevice() {
return device;
}