Skip to content

Commit

Permalink
v2.2.1 (build 242)
Browse files Browse the repository at this point in the history
  • Loading branch information
finiasz committed Jun 10, 2024
1 parent fab722c commit 3dd0ca5
Show file tree
Hide file tree
Showing 63 changed files with 931 additions and 459 deletions.
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,21 @@
# Build 242 (2.2.1)
2024-06-10

- Fix a multi-profile issue with bookmarked messages

# ~~Build 241 (2.2.1)~~
2024-06-06

- Display an explanation of the message status in message details
- Fix message count taking too long in global search

# ~~Build 239 (2.2.1)~~
2024-06-04

- Better handling of messages received out-of-order (typically via WebSocket while a listing is in progress)
- Implement a few tweaks in cryptographic engine
- Some engine db optimizations

# Build 238 (2.2)
2024-06-03

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ public void decryptAndProcess(NetworkReceivedMessage networkReceivedMessage) {
// we were not able to decrypt the message -> we delete it
if (channelManagerSession.networkFetchDelegate != null) {
Logger.d("The message cannot be decrypted.");
channelManagerSession.networkFetchDelegate.deleteMessageAndAttachments(channelManagerSession.session, networkReceivedMessage.getOwnedIdentity(), networkReceivedMessage.getMessageUid());
channelManagerSession.networkFetchDelegate.messageCannotBeDecrypted(channelManagerSession.session, networkReceivedMessage.getOwnedIdentity(), networkReceivedMessage.getMessageUid());
channelManagerSession.session.commit();
} else {
Logger.w("Unable to delete a networkReceivedMessage because the NetworkFetchDelegate is not set yet.");
Expand All @@ -87,7 +87,7 @@ private void decryptAndProcess(ChannelManagerSession channelManagerSession, Netw
}
ChannelReceivedMessage channelReceivedMessage;
try {
channelReceivedMessage = new ChannelReceivedMessage(networkReceivedMessage, authEncKeyAndChannelInfo.getAuthEncKey(), authEncKeyAndChannelInfo.getReceptionChannelInfo());
channelReceivedMessage = new ChannelReceivedMessage(channelManagerSession, networkReceivedMessage, authEncKeyAndChannelInfo.getAuthEncKey(), authEncKeyAndChannelInfo.getReceptionChannelInfo());
} catch (Exception e) {
channelManagerSession.networkFetchDelegate.deleteMessageAndAttachments(channelManagerSession.session, networkReceivedMessage.getOwnedIdentity(), networkReceivedMessage.getMessageUid());
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,14 @@ public class ObliviousChannel extends NetworkChannel implements ObvDatabase {
private boolean fullRatchetOfTheSendSeedInProgress;
static final String FULL_RATCHET_OF_THE_SEND_SEED_IN_PROGRESS = "full_ratchet_of_the_send_seed_in_progress";

// info for GKMV2
private boolean supportsGKMV2;
static final String SUPPORTS_GKMV_2 = "supports_gkmv_2";
private int fullRatchetingCountForGkmv2Support;
static final String FULL_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT = "full_ratcheting_count_with_gkmv_2_support";
private int selfRatchetingCountForGkmv2Support;
static final String SELF_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT = "self_ratcheting_count_with_gkmv_2_support";


public UID getCurrentDeviceUid() {
return currentDeviceUid;
Expand All @@ -118,6 +126,12 @@ public ReceptionChannelInfo getReceptionChannelInfo() {
return ReceptionChannelInfo.createObliviousChannelInfo(remoteDeviceUid, remoteIdentity);
}

private boolean supportsGKMV2(int fullRatchetingCount, int selfRatchetingCount) {
return supportsGKMV2
&& (fullRatchetingCount > fullRatchetingCountForGkmv2Support
|| (fullRatchetingCount == fullRatchetingCountForGkmv2Support && selfRatchetingCount > selfRatchetingCountForGkmv2Support));
}

public int getNumberOfEncryptedMessagesSinceLastFullRatchet() {
return numberOfEncryptedMessages - numberOfEncryptedMessagesAtTheTimeOfTheLastFullRatchet;
}
Expand Down Expand Up @@ -203,6 +217,28 @@ public void confirm() throws SQLException {
}
}

public static void setSupportsGKMV2(ChannelManagerSession channelManagerSession, UID currentDeviceUid, UID remoteDeviceUid, Identity remoteIdentity, int fullRatchetingCount, int selfRatchetingCount) throws SQLException {
ObliviousChannel obliviousChannel = get(channelManagerSession, currentDeviceUid, remoteDeviceUid, remoteIdentity, false);
if (obliviousChannel.supportsGKMV2(fullRatchetingCount, selfRatchetingCount)) {
// the oblivious channel is already tagged as supporting GKMV2 at an older full/self ratcheting count
return;
}
try (PreparedStatement statement = channelManagerSession.session.prepareStatement("UPDATE " + TABLE_NAME + " SET " +
SUPPORTS_GKMV_2 + " = 1, " +
FULL_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT + " = ?," +
SELF_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT + " = ? " +
" WHERE " + CURRENT_DEVICE_UID + " = ? " +
" AND " + REMOTE_DEVICE_UID + " = ? " +
" AND " + REMOTE_IDENTITY + " = ?;")) {
statement.setInt(1, fullRatchetingCount);
statement.setInt(2, selfRatchetingCount);
statement.setBytes(3, currentDeviceUid.getBytes());
statement.setBytes(4, remoteDeviceUid.getBytes());
statement.setBytes(5, remoteIdentity.getBytes());
statement.executeUpdate();
}
}

// This method is called after a send full ratchet
public void updateSendSeed(Seed seed, int obliviousEngineVersion) {
Seed sendSeed = generateDiversifiedSeed(seed, currentDeviceUid, obliviousEngineVersion);
Expand Down Expand Up @@ -326,6 +362,10 @@ private ObliviousChannel(ChannelManagerSession channelManagerSession,
this.timestampOfLastFullRatchet = System.currentTimeMillis();
this.timestampOfLastFullRatchetSentMessage = this.timestampOfLastFullRatchet;
this.fullRatchetOfTheSendSeedInProgress = false;
this.supportsGKMV2 = false;

this.fullRatchetingCountForGkmv2Support = -1;
this.selfRatchetingCountForGkmv2Support = -1;
}

private ObliviousChannel(ChannelManagerSession channelManagerSession, ResultSet res) throws SQLException {
Expand All @@ -351,6 +391,10 @@ private ObliviousChannel(ChannelManagerSession channelManagerSession, ResultSet
this.timestampOfLastFullRatchet = res.getLong(TIMESTAMP_OF_LAST_FULL_RATCHET);
this.timestampOfLastFullRatchetSentMessage = res.getLong(TIMESTAMP_OF_LAST_FULL_RATCHET_SENT_MESSAGE);
this.fullRatchetOfTheSendSeedInProgress = res.getBoolean(FULL_RATCHET_OF_THE_SEND_SEED_IN_PROGRESS);
this.supportsGKMV2 = res.getBoolean(SUPPORTS_GKMV_2);

this.fullRatchetingCountForGkmv2Support = res.getInt(FULL_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT);
this.selfRatchetingCountForGkmv2Support = res.getInt(SELF_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT);
}


Expand All @@ -372,16 +416,28 @@ public static void createTable(Session session) throws SQLException {
TIMESTAMP_OF_LAST_FULL_RATCHET + " BIGINT NOT NULL, " +
TIMESTAMP_OF_LAST_FULL_RATCHET_SENT_MESSAGE + " BIGINT NOT NULL, " +
FULL_RATCHET_OF_THE_SEND_SEED_IN_PROGRESS + " BIT NOT NULL, " +
SUPPORTS_GKMV_2 + " BIT NOT NULL, " +
FULL_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT + " INT NOT NULL, " +
SELF_RATCHETING_COUNT_FOR_GKMV_2_SUPPORT + " INT NOT NULL, " +
"CONSTRAINT PK_" + TABLE_NAME + " PRIMARY KEY(" + CURRENT_DEVICE_UID + ", " + REMOTE_DEVICE_UID +", " + REMOTE_IDENTITY + "));");
}
}

public static void upgradeTable(Session session, int oldVersion, int newVersion) throws SQLException {
if (oldVersion < 39 && newVersion >= 39) {
Logger.d("MIGRATING `oblivious_channel` DATABASE FROM VERSION " + oldVersion + " TO 39");
try (Statement statement = session.createStatement()) {
statement.execute("ALTER TABLE oblivious_channel ADD COLUMN `supports_gkmv_2` BIT NOT NULL DEFAULT 0");
statement.execute("ALTER TABLE oblivious_channel ADD COLUMN `full_ratcheting_count_with_gkmv_2_support` INT NOT NULL DEFAULT -1");
statement.execute("ALTER TABLE oblivious_channel ADD COLUMN `self_ratcheting_count_with_gkmv_2_support` INT NOT NULL DEFAULT -1");
}
oldVersion = 39;
}
}

@Override
public void insert() throws SQLException {
try (PreparedStatement statement = channelManagerSession.session.prepareStatement("INSERT INTO " + TABLE_NAME + " VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?,?);")) {
try (PreparedStatement statement = channelManagerSession.session.prepareStatement("INSERT INTO " + TABLE_NAME + " VALUES (?,?,?,?,?, ?,?,?,?,?, ?,?,?,?,?, ?,?);")) {
statement.setBytes(1, currentDeviceUid.getBytes());
statement.setBytes(2, remoteDeviceUid.getBytes());
statement.setBytes(3, remoteIdentity.getBytes());
Expand All @@ -398,6 +454,10 @@ public void insert() throws SQLException {
statement.setLong(12, timestampOfLastFullRatchet);
statement.setLong(13, timestampOfLastFullRatchetSentMessage);
statement.setBoolean(14, fullRatchetOfTheSendSeedInProgress);
statement.setBoolean(15, supportsGKMV2);

statement.setInt(16, fullRatchetingCountForGkmv2Support);
statement.setInt(17, selfRatchetingCountForGkmv2Support);
statement.executeUpdate();
}
}
Expand Down Expand Up @@ -430,14 +490,12 @@ public static ObliviousChannel get(ChannelManagerSession channelManagerSession,
try (ResultSet res = statement.executeQuery()) {
if (res.next()) {
return new ObliviousChannel(channelManagerSession, res);
} else {
return null;
}
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
return null;
}

public static ObliviousChannel[] getAll(ChannelManagerSession channelManagerSession) {
Expand Down Expand Up @@ -823,7 +881,10 @@ public static AuthEncKeyAndChannelInfo unwrapMessageKey(ChannelManagerSession ch
} catch (SQLException e) {
e.printStackTrace();
}
return new AuthEncKeyAndChannelInfo(messageKey, obliviousChannel.getReceptionChannelInfo());
ReceptionChannelInfo receptionChannelInfo = obliviousChannel.getReceptionChannelInfo();
// add information about GKMV2 in receptionChannelInfo
receptionChannelInfo.enrichWithGKMV2Info(provisionedKey.getProvisionFullRatchetingCount(), provisionedKey.getSelfRatchetingCount(), obliviousChannel.supportsGKMV2(provisionedKey.getProvisionFullRatchetingCount(), provisionedKey.getSelfRatchetingCount()));
return new AuthEncKeyAndChannelInfo(messageKey, receptionChannelInfo);
} catch (InvalidKeyException | DecryptionException | DecodingException | ClassCastException e) {
// nothing to do
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
package io.olvid.engine.channel.datatypes;


import io.olvid.engine.Logger;
import io.olvid.engine.channel.databases.ObliviousChannel;
import io.olvid.engine.crypto.AuthEnc;
import io.olvid.engine.crypto.PRNG;
import io.olvid.engine.crypto.Suite;
Expand All @@ -39,12 +41,30 @@ public class ChannelReceivedMessage {
private final ReceptionChannelInfo receptionChannelInfo;
private final NetworkReceivedMessage message;

public ChannelReceivedMessage(NetworkReceivedMessage message, AuthEncKey messageKey, ReceptionChannelInfo receptionChannelInfo) throws Exception {
public ChannelReceivedMessage(ChannelManagerSession channelManagerSession, NetworkReceivedMessage message, AuthEncKey messageKey, ReceptionChannelInfo receptionChannelInfo) throws Exception {
try {
// decrypt
AuthEnc authEnc = Suite.getAuthEnc(messageKey);
Encoded decryptedMessage = new Encoded(authEnc.decrypt(messageKey, message.getEncryptedContent()));

// verify the messageKey is properly formatted
boolean messageKeyCheckPassed = authEnc.verifyMessageKey(messageKey, decryptedMessage.getBytes());
Logger.d("MessageKey check: " + (messageKeyCheckPassed ? "PASSED" : "FAILED"));
if (receptionChannelInfo.getChannelType() == ReceptionChannelInfo.OBLIVIOUS_CHANNEL_TYPE) {
// check the GKMV2 info in receptionChannelInfo
if (receptionChannelInfo.obliviousChannelsSupportsGKMV2() && !messageKeyCheckPassed) {
Logger.e("Received a message not passing the messageKey check on an oblivious channel that supports GKMV2. Discarding it!!!!");
throw new Exception();
} else if (messageKeyCheckPassed && !receptionChannelInfo.obliviousChannelsSupportsGKMV2()) {
// received a message that passes the GKMV2 messageKey check --> tag the ObliviousChannel
UID currentDeviceUid = channelManagerSession.identityDelegate.getCurrentDeviceUidOfOwnedIdentity(channelManagerSession.session, message.getHeader().getOwnedIdentity());
if (currentDeviceUid != null) {
Logger.i("Tagging an oblivious channel as supporting GKMV2");
ObliviousChannel.setSupportsGKMV2(channelManagerSession, currentDeviceUid, receptionChannelInfo.getRemoteDeviceUid(), receptionChannelInfo.getRemoteIdentity(), receptionChannelInfo.getFullRatchetCount(), receptionChannelInfo.getSelfRatchetCount());
}
}
}

// if needed, compute the extended payload key
if (message.hasExtendedPayload()) {
PRNG extendedPayloadPRNG = Suite.getDefaultPRNG(0, Seed.of(messageKey));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,22 +79,6 @@ public static UID post(ChannelManagerSession channelManagerSession, ChannelMessa
}

AuthEnc authEnc = Suite.getDefaultAuthEnc(suiteVersion);
AuthEncKey messageKey = authEnc.generateKey(prng);

MessageToSend.Header[] headers = new MessageToSend.Header[networkChannels.length];
boolean partOfFullRatchetProtocol = (message instanceof ChannelProtocolMessageToSend) && ((ChannelProtocolMessageToSend) message).isPartOfFullRatchetProtocolOfTheSendSeed();
for (int i=0; i<networkChannels.length; i++) {
headers[i] = networkChannels[i].wrapMessageKey(messageKey, prng, partOfFullRatchetProtocol);
}

// check that all headers are for the same server
String server = headers[0].getToIdentity().getServer();
for (int i=1; i<headers.length; i++) {
if (! server.equals(headers[i].getToIdentity().getServer())) {
Logger.w("Server mismatch in the headers of a ChannelMessageToSend");
throw new Exception();
}
}

MessageToSend messageToSend;
UID messageUid = new UID(prng);
Expand Down Expand Up @@ -124,13 +108,21 @@ public static UID post(ChannelManagerSession channelManagerSession, ChannelMessa
Encoded.of(MessageType.APPLICATION_MESSAGE_TYPE),
Encoded.of(listOfEncodedAttachments)
});

////////
// Add a padding to message to obfuscate content length. Commented out for now
// TODO: uncomment once all clients can handle padded messages
// byte[] paddedPlaintext = new byte[((plaintextContent.getBytes().length - 1) | 511) + 1];
// System.arraycopy(plaintextContent.getBytes(), 0, paddedPlaintext, 0, plaintextContent.getBytes().length);
// EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, paddedPlaintext, prng);
EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, plaintextContent.getBytes(), prng);
byte[] paddedPlaintext = new byte[((plaintextContent.getBytes().length - 1) | 511) + 1];
System.arraycopy(plaintextContent.getBytes(), 0, paddedPlaintext, 0, plaintextContent.getBytes().length);

AuthEncKey messageKey = authEnc.generateMessageKey(prng, paddedPlaintext);

MessageToSend.Header[] headers = generateHeaders(networkChannels, false, messageKey, prng);

// check that all headers are for the same server
String server = getServer(headers);


EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, paddedPlaintext, prng);

final EncryptedBytes encryptedExtendedContent;
if (channelApplicationMessageToSend.getExtendedMessagePayload() != null) {
Expand All @@ -153,13 +145,21 @@ public static UID post(ChannelManagerSession channelManagerSession, ChannelMessa
Encoded.of(MessageType.PROTOCOL_MESSAGE_TYPE),
channelProtocolMessageToSend.getEncodedElements()
});

////////
// Add a padding to message to obfuscate content length. Commented out for now
// TODO: uncomment once all clients can handle padded messages
// byte[] paddedPlaintext = new byte[((plaintextContent.getBytes().length - 1) | 511) + 1];
// System.arraycopy(plaintextContent.getBytes(), 0, paddedPlaintext, 0, plaintextContent.getBytes().length);
// EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, paddedPlaintext, prng);
EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, plaintextContent.getBytes(), prng);
byte[] paddedPlaintext = new byte[((plaintextContent.getBytes().length - 1) | 511) + 1];
System.arraycopy(plaintextContent.getBytes(), 0, paddedPlaintext, 0, plaintextContent.getBytes().length);

AuthEncKey messageKey = authEnc.generateMessageKey(prng, paddedPlaintext);

MessageToSend.Header[] headers = generateHeaders(networkChannels, channelProtocolMessageToSend.isPartOfFullRatchetProtocolOfTheSendSeed(), messageKey, prng);

// check that all headers are for the same server
String server = getServer(headers);


EncryptedBytes encryptedContent = authEnc.encrypt(messageKey, paddedPlaintext, prng);
messageToSend = new MessageToSend(message.getSendChannelInfo().getFromIdentity(), messageUid, server, encryptedContent, headers);
break;
}
Expand All @@ -170,4 +170,25 @@ public static UID post(ChannelManagerSession channelManagerSession, ChannelMessa
channelManagerSession.networkSendDelegate.post(channelManagerSession.session, messageToSend);
return messageUid;
}

private static MessageToSend.Header[] generateHeaders(NetworkChannel[] networkChannels, boolean partOfFullRatchetProtocol, AuthEncKey messageKey, PRNGService prng) {
MessageToSend.Header[] headers = new MessageToSend.Header[networkChannels.length];
for (int i=0; i<networkChannels.length; i++) {
headers[i] = networkChannels[i].wrapMessageKey(messageKey, prng, partOfFullRatchetProtocol);
}

return headers;
}

private static String getServer(MessageToSend.Header[] headers) throws Exception {
// check that all headers are for the same server
String server = headers[0].getToIdentity().getServer();
for (int i=1; i<headers.length; i++) {
if (!server.equals(headers[i].getToIdentity().getServer())) {
Logger.w("Server mismatch in the headers of a ChannelMessageToSend");
throw new Exception();
}
}
return server;
}
}
Loading

0 comments on commit 3dd0ca5

Please sign in to comment.