Skip to content

Commit

Permalink
v3.0 (build 248)
Browse files Browse the repository at this point in the history
  • Loading branch information
finiasz committed Jul 8, 2024
1 parent 3dd0ca5 commit ee9d445
Show file tree
Hide file tree
Showing 195 changed files with 8,592 additions and 1,272 deletions.
30 changes: 30 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
# Build 248 (3.0)
2024-07-05

- Remove Android Auto support because app was rejected from the store...
- Update WebRTC

# ~~Build 247 (3.0)~~
2024-06-26

- Change the way phone call ringing timeouts are handled
- Truncate messages ending with a link for which a preview is available
- Add a global location sharing map, with all locations currently shared with you (in all discussions)

# ~~Build 246 (2.3)~~
2024-06-22

- Multiple small bugs fixed

# ~~Build 245 (2.3)~~
2024-06-19

- Make some engine operations more "bulletproof"

# ~~Build 243 (2.3)~~
2024-06-18

- Support for pre-keys allowing to receive messages before the end of the channel creation
- Optimize device discovery and channel creation attempts
- Detect contact that have been offline for an extended period of time

# Build 242 (2.2.1)
2024-06-10

Expand Down

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,24 @@


import java.sql.SQLException;
import java.util.EnumSet;
import java.util.Objects;

import io.olvid.engine.Logger;
import io.olvid.engine.channel.databases.ObliviousChannel;
import io.olvid.engine.channel.datatypes.AsymmetricChannel;
import io.olvid.engine.channel.datatypes.AuthEncKeyAndChannelInfo;
import io.olvid.engine.channel.datatypes.PreKeyChannel;
import io.olvid.engine.datatypes.Identity;
import io.olvid.engine.datatypes.containers.AuthEncKeyAndChannelInfo;
import io.olvid.engine.channel.datatypes.ChannelManagerSession;
import io.olvid.engine.channel.datatypes.ChannelManagerSessionFactory;
import io.olvid.engine.channel.datatypes.ChannelReceivedApplicationMessage;
import io.olvid.engine.channel.datatypes.ChannelReceivedMessage;
import io.olvid.engine.datatypes.containers.MessageType;
import io.olvid.engine.datatypes.containers.NetworkReceivedMessage;
import io.olvid.engine.datatypes.containers.ProtocolReceivedMessage;
import io.olvid.engine.datatypes.containers.ReceptionChannelInfo;
import io.olvid.engine.engine.types.identities.ObvContactActiveOrInactiveReason;

public class ChannelCoordinator {
private final ChannelManagerSessionFactory channelManagerSessionFactory;
Expand All @@ -57,6 +63,15 @@ public void decryptAndProcess(NetworkReceivedMessage networkReceivedMessage) {
return;
}

// try to decrypt with a PreKey
authEncKeyAndChannelInfo = PreKeyChannel.unwrapMessageKey(channelManagerSession, networkReceivedMessage.getHeader());
if (authEncKeyAndChannelInfo != null) {
Logger.d("The message can be decrypted with a PreKey. ");
decryptAndProcess(channelManagerSession, networkReceivedMessage, authEncKeyAndChannelInfo);
channelManagerSession.session.commit();
return;
}

// try to decrypt with an AsymmetricChannel
authEncKeyAndChannelInfo = AsymmetricChannel.unwrapMessageKey(channelManagerSession, networkReceivedMessage.getHeader());
if (authEncKeyAndChannelInfo != null) {
Expand Down Expand Up @@ -93,6 +108,35 @@ private void decryptAndProcess(ChannelManagerSession channelManagerSession, Netw
return;
}

// for preKey encrypted messages, check that the contact exists, otherwise, put the message on hold until the contact is added
if (channelReceivedMessage.getReceptionChannelInfo().getChannelType() == ReceptionChannelInfo.PRE_KEY_CHANNEL_TYPE) {
Identity ownedIdentity = networkReceivedMessage.getOwnedIdentity();
Identity contactIdentity = channelReceivedMessage.getReceptionChannelInfo().getRemoteIdentity();
if (!Objects.equals(ownedIdentity, contactIdentity)) {
try {
// the message is from a contact
if (!channelManagerSession.identityDelegate.isIdentityAContactOfOwnedIdentity(channelManagerSession.session, ownedIdentity, contactIdentity)) {
// contact unknown, set the from identity of the inbox message to reprocess it once the contact is created
Logger.i("Received a PreKey encrypted message from an unknown contact, putting it on hold...");
channelManagerSession.networkFetchDelegate.setInboxMessageFromIdentityForMissingPreKeyContact(channelManagerSession.session, networkReceivedMessage.getOwnedIdentity(), networkReceivedMessage.getMessageUid(), contactIdentity);
return;
} else {
EnumSet<ObvContactActiveOrInactiveReason> reasons = channelManagerSession.identityDelegate.getContactActiveOrInactiveReasons(channelManagerSession.session, ownedIdentity, contactIdentity);
if (reasons != null && reasons.contains(ObvContactActiveOrInactiveReason.REVOKED) && !reasons.contains(ObvContactActiveOrInactiveReason.FORCEFULLY_UNBLOCKED)) {
// the contact is blocked, discard the message
Logger.w("Received a PreKey encrypted message from a blocked contact, discarding it!");
channelManagerSession.networkFetchDelegate.deleteMessageAndAttachments(channelManagerSession.session, networkReceivedMessage.getOwnedIdentity(), networkReceivedMessage.getMessageUid());
return;
} else if (!channelManagerSession.identityDelegate.isContactDeviceKnown(channelManagerSession.session, ownedIdentity, contactIdentity, channelReceivedMessage.getReceptionChannelInfo().getRemoteDeviceUid())) {
channelManagerSession.protocolStarterDelegate.startDeviceDiscoveryProtocolWithinTransaction(channelManagerSession.session, ownedIdentity, contactIdentity);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

switch (channelReceivedMessage.getMessageType()) {
case MessageType.PROTOCOL_MESSAGE_TYPE:
if (channelManagerSession.protocolDelegate == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,16 @@
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;

import io.olvid.engine.Logger;
import io.olvid.engine.channel.datatypes.AuthEncKeyAndChannelInfo;
import io.olvid.engine.channel.datatypes.PreKeyChannel;
import io.olvid.engine.datatypes.containers.AuthEncKeyAndChannelInfo;
import io.olvid.engine.channel.datatypes.ChannelManagerSession;
import io.olvid.engine.channel.datatypes.NetworkChannel;
import io.olvid.engine.channel.datatypes.RatchetingOutput;
Expand All @@ -55,8 +58,10 @@
import io.olvid.engine.datatypes.containers.MessageToSend;
import io.olvid.engine.datatypes.containers.MessageType;
import io.olvid.engine.datatypes.containers.NetworkReceivedMessage;
import io.olvid.engine.datatypes.containers.OwnedDeviceAndPreKey;
import io.olvid.engine.datatypes.containers.ReceptionChannelInfo;
import io.olvid.engine.datatypes.containers.SendChannelInfo;
import io.olvid.engine.datatypes.containers.UidAndPreKey;
import io.olvid.engine.datatypes.key.symmetric.AuthEncKey;
import io.olvid.engine.datatypes.notifications.ChannelNotifications;
import io.olvid.engine.encoder.DecodingException;
Expand Down Expand Up @@ -700,7 +705,7 @@ private static Seed generateDiversifiedSeed(Seed seed, UID uid, int obliviousEng



public static ObliviousChannel[] acceptableChannelsForPosting(ChannelManagerSession channelManagerSession, ChannelMessageToSend message) throws SQLException {
public static NetworkChannel[] acceptableChannelsForPosting(ChannelManagerSession channelManagerSession, ChannelMessageToSend message) throws SQLException {
if (channelManagerSession.identityDelegate == null) {
Logger.w("Calling acceptableChannelsForPosting with no IdentityDelegate set.");
return new ObliviousChannel[0];
Expand All @@ -723,7 +728,7 @@ public static ObliviousChannel[] acceptableChannelsForPosting(ChannelManagerSess
remoteDeviceUidSet.retainAll(Arrays.asList(message.getSendChannelInfo().getRemoteDeviceUids()));
UID currentDeviceUid = channelManagerSession.identityDelegate.getCurrentDeviceUidOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());

ObliviousChannel[] channels = ObliviousChannel.getAcceptableObliviousChannels(channelManagerSession, currentDeviceUid, remoteDeviceUidSet.toArray(new UID[0]), message.getSendChannelInfo().getToIdentity(), message.getSendChannelInfo().getNecessarilyConfirmed());
ObliviousChannel[] channels = ObliviousChannel.getAcceptableObliviousChannels(channelManagerSession, currentDeviceUid, remoteDeviceUidSet.toArray(new UID[0]), message.getSendChannelInfo().getToIdentity(), message.getSendChannelInfo().getNecessarilyConfirmed()).toArray(new ObliviousChannel[0]);

if (message.getMessageType() == MessageType.PROTOCOL_MESSAGE_TYPE) {
if (((ChannelProtocolMessageToSend) message).isPartOfFullRatchetProtocolOfTheSendSeed()) {
Expand All @@ -734,42 +739,101 @@ public static ObliviousChannel[] acceptableChannelsForPosting(ChannelManagerSess
}
return channels;
}
case SendChannelInfo.ALL_CONFIRMED_OBLIVIOUS_CHANNELS_ON_SAME_SERVER_TYPE: {
List<ObliviousChannel> acceptableChannels = new ArrayList<>();
case SendChannelInfo.ALL_CONFIRMED_OBLIVIOUS_CHANNELS_OR_PRE_KEY_ON_SAME_SERVER_TYPE: {
List<NetworkChannel> acceptableChannels = new ArrayList<>();
UID currentDeviceUid = channelManagerSession.identityDelegate.getCurrentDeviceUidOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
for (Identity toIdentity: message.getSendChannelInfo().getToIdentities()) {
final UID[] remoteDeviceUids;
List<UidAndPreKey> uidsAndPreKeys = new ArrayList<>();
if (Objects.equals(message.getSendChannelInfo().getFromIdentity(), toIdentity)) {
remoteDeviceUids = channelManagerSession.identityDelegate.getOtherDeviceUidsOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
List<OwnedDeviceAndPreKey> ownedDeviceAndPreKeys = channelManagerSession.identityDelegate.getDevicesAndPreKeysOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
for (OwnedDeviceAndPreKey ownedDeviceAndPreKey : ownedDeviceAndPreKeys) {
if (!ownedDeviceAndPreKey.currentDevice) {
uidsAndPreKeys.add(new UidAndPreKey(ownedDeviceAndPreKey.deviceUid, ownedDeviceAndPreKey.preKey));
}
}
} else {
remoteDeviceUids = channelManagerSession.identityDelegate.getDeviceUidsOfContactIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity(), toIdentity);
uidsAndPreKeys = channelManagerSession.identityDelegate.getDeviceUidsAndPreKeysOfContactIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity(), toIdentity);
}
acceptableChannels.addAll(ObliviousChannel.getAcceptableObliviousOrPreKeyChannels(channelManagerSession, message.getSendChannelInfo().getFromIdentity(), currentDeviceUid, uidsAndPreKeys.toArray(new UidAndPreKey[0]), toIdentity));
}
return acceptableChannels.toArray(new NetworkChannel[0]);
}
case SendChannelInfo.ALL_OWNED_CONFIRMED_OBLIVIOUS_CHANNELS_OR_PRE_KEY_TYPE: {
List<UidAndPreKey> uidsAndPreKeys = new ArrayList<>();
List<OwnedDeviceAndPreKey> ownedDeviceAndPreKeys = channelManagerSession.identityDelegate.getDevicesAndPreKeysOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
for (OwnedDeviceAndPreKey ownedDeviceAndPreKey : ownedDeviceAndPreKeys) {
if (!ownedDeviceAndPreKey.currentDevice) {
uidsAndPreKeys.add(new UidAndPreKey(ownedDeviceAndPreKey.deviceUid, ownedDeviceAndPreKey.preKey));
}
acceptableChannels.addAll(Arrays.asList(ObliviousChannel.getAcceptableObliviousChannels(channelManagerSession, currentDeviceUid, remoteDeviceUids, toIdentity, true)));
}
return acceptableChannels.toArray(new ObliviousChannel[0]);

UID currentDeviceUid = channelManagerSession.identityDelegate.getCurrentDeviceUidOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
return ObliviousChannel.getAcceptableObliviousOrPreKeyChannels(channelManagerSession, message.getSendChannelInfo().getFromIdentity(), currentDeviceUid, uidsAndPreKeys.toArray(new UidAndPreKey[0]), message.getSendChannelInfo().getToIdentity()).toArray(new NetworkChannel[0]);
}
case SendChannelInfo.ALL_OWNED_CONFIRMED_OBLIVIOUS_CHANNELS_TYPE: {
UID[] otherDeviceUids = channelManagerSession.identityDelegate.getOtherDeviceUidsOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
case SendChannelInfo.OBLIVIOUS_CHANNEL_OR_PRE_KEY_TYPE: {
List<UidAndPreKey> uidsAndPreKeys = new ArrayList<>();
if (Objects.equals(message.getSendChannelInfo().getFromIdentity(), message.getSendChannelInfo().getToIdentity())) {
List<OwnedDeviceAndPreKey> ownedDeviceAndPreKeys = channelManagerSession.identityDelegate.getDevicesAndPreKeysOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
for (OwnedDeviceAndPreKey ownedDeviceAndPreKey : ownedDeviceAndPreKeys) {
if (!ownedDeviceAndPreKey.currentDevice) {
uidsAndPreKeys.add(new UidAndPreKey(ownedDeviceAndPreKey.deviceUid, ownedDeviceAndPreKey.preKey));
}
}
} else {
uidsAndPreKeys = channelManagerSession.identityDelegate.getDeviceUidsAndPreKeysOfContactIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity(), message.getSendChannelInfo().getToIdentity());
}

HashSet<UID> remoteUids = new HashSet<>(Arrays.asList(message.getSendChannelInfo().getRemoteDeviceUids()));
List<UidAndPreKey> remoteUidsAndPreKeys = new ArrayList<>();
for (UidAndPreKey uidAndPreKey : uidsAndPreKeys) {
if (remoteUids.contains(uidAndPreKey.uid)) {
remoteUidsAndPreKeys.add(uidAndPreKey);
}
}

UID currentDeviceUid = channelManagerSession.identityDelegate.getCurrentDeviceUidOfOwnedIdentity(channelManagerSession.session, message.getSendChannelInfo().getFromIdentity());
return ObliviousChannel.getAcceptableObliviousChannels(channelManagerSession, currentDeviceUid, otherDeviceUids, message.getSendChannelInfo().getToIdentity(), true);
return ObliviousChannel.getAcceptableObliviousOrPreKeyChannels(channelManagerSession, message.getSendChannelInfo().getFromIdentity(), currentDeviceUid, remoteUidsAndPreKeys.toArray(new UidAndPreKey[0]), message.getSendChannelInfo().getToIdentity()).toArray(new NetworkChannel[0]);
}
default:
return new ObliviousChannel[0];
}
}

private static ObliviousChannel[] getAcceptableObliviousChannels(ChannelManagerSession channelManagerSession, UID currentDeviceUid, UID[] remoteDeviceUids, Identity remoteIdentity, boolean necessarilyConfirmed) {
private static List<NetworkChannel> getAcceptableObliviousOrPreKeyChannels(ChannelManagerSession channelManagerSession, Identity ownedIdentity, UID currentDeviceUid, UidAndPreKey[] remoteDeviceUidsAndPreKeys, Identity remoteIdentity) {
// first get all oblivious channels
UID[] uids = new UID[remoteDeviceUidsAndPreKeys.length];
for (int i=0; i<remoteDeviceUidsAndPreKeys.length; i++) {
uids[i] = remoteDeviceUidsAndPreKeys[i].uid;
}
List<ObliviousChannel> obliviousChannels = getAcceptableObliviousChannels(channelManagerSession, currentDeviceUid, uids, remoteIdentity, true);
HashSet<UID> obliviousChannelUids = new HashSet<>();
for (ObliviousChannel obliviousChannel : obliviousChannels) {
obliviousChannelUids.add(obliviousChannel.remoteDeviceUid);
}

List<NetworkChannel> acceptableChannels = new ArrayList<>();
for (UidAndPreKey uidAndPreKey : remoteDeviceUidsAndPreKeys) {
if (!obliviousChannelUids.contains(uidAndPreKey.uid) && uidAndPreKey.preKey != null) {
acceptableChannels.add(new PreKeyChannel(channelManagerSession.session, ownedIdentity, remoteIdentity, uidAndPreKey.uid, channelManagerSession.preKeyEncryptionDelegate));
}
}

acceptableChannels.addAll(obliviousChannels);
return acceptableChannels;
}

private static List<ObliviousChannel> getAcceptableObliviousChannels(ChannelManagerSession channelManagerSession, UID currentDeviceUid, UID[] remoteDeviceUids, Identity remoteIdentity, boolean necessarilyConfirmed) {
ObliviousChannel[] channels = getMany(channelManagerSession, currentDeviceUid, remoteDeviceUids, remoteIdentity, necessarilyConfirmed);
if (channels == null) {
return new ObliviousChannel[0];
return Collections.emptyList();
}
List<ObliviousChannel> channelList = new ArrayList<>();
for (ObliviousChannel channel: channels) {
if (channel.getObliviousEngineVersion() >= Suite.MINIMUM_ACCEPTABLE_VERSION) {
channelList.add(channel);
}
}
return channelList.toArray(new ObliviousChannel[0]);
return channelList;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,17 +124,16 @@ public void selfRatchetIfRequired() {
}

public static void deleteAllEmpty(ChannelManagerSession channelManagerSession) {
// loop over all Provision, and count the number of ProvisionedKeyMaterial for each of them:
// delete those with 0 ProvisionedKeyMaterial.
try (Statement statement = channelManagerSession.session.createStatement()) {
try (ResultSet res = statement.executeQuery("SELECT * FROM " + TABLE_NAME)) {
while (res.next()) {
Provision provision = new Provision(channelManagerSession, res);
if (ProvisionedKeyMaterial.countProvisionedReceiveKey(channelManagerSession, provision) == 0) {
provision.delete();
}
}
}
// delete all Provision, with no ProvisionedKeyMaterial.
try (PreparedStatement statement = channelManagerSession.session.prepareStatement("DELETE FROM " + TABLE_NAME + " AS p " +
" WHERE NOT EXISTS (" +
" SELECT 1 FROM " + ProvisionedKeyMaterial.TABLE_NAME +
" WHERE " + ProvisionedKeyMaterial.PROVISION_FULL_RATCHETING_COUNT + " = p." + FULL_RATCHETING_COUNT +
" AND " + ProvisionedKeyMaterial.PROVISION_OBLIVIOUS_CHANNEL_CURRENT_DEVICE_UID + " = p." + OBLIVIOUS_CHANNEL_CURRENT_DEVICE_UID +
" AND " + ProvisionedKeyMaterial.PROVISION_OBLIVIOUS_CHANNEL_REMOTE_DEVICE_UID + " = p." + OBLIVIOUS_CHANNEL_REMOTE_DEVICE_UID +
" AND " + ProvisionedKeyMaterial.PROVISION_OBLIVIOUS_CHANNEL_REMOTE_IDENTITY + " = p." + OBLIVIOUS_CHANNEL_REMOTE_IDENTITY +
")")) {
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
Expand Down
Loading

0 comments on commit ee9d445

Please sign in to comment.