From 7c820f7b32aca413a0d1d14b125c37ccf3761e4c Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Sat, 11 Mar 2023 15:56:17 +0100 Subject: [PATCH] create stub message contents for encryption failures --- .../android/axolotl/AxolotlService.java | 27 ++++++++++++++--- .../android/database/model/Encryption.java | 1 + .../android/database/model/PartType.java | 4 ++- .../transformer/MessageContentWrapper.java | 30 +++++++++++++++++++ .../android/transformer/Transformer.java | 20 +++++-------- .../android/xmpp/manager/ArchiveManager.java | 5 ++-- .../xmpp/processor/MessageProcessor.java | 11 +++---- 7 files changed, 71 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/im/conversations/android/axolotl/AxolotlService.java b/app/src/main/java/im/conversations/android/axolotl/AxolotlService.java index 2fae09d7f..616fab860 100644 --- a/app/src/main/java/im/conversations/android/axolotl/AxolotlService.java +++ b/app/src/main/java/im/conversations/android/axolotl/AxolotlService.java @@ -3,6 +3,7 @@ package im.conversations.android.axolotl; import android.content.Context; import android.os.Build; import com.google.common.base.Optional; +import com.google.common.base.Preconditions; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.ImmutableSet; @@ -12,6 +13,7 @@ import im.conversations.android.AbstractAccountService; import im.conversations.android.database.AxolotlDatabaseStore; import im.conversations.android.database.ConversationsDatabase; import im.conversations.android.database.model.Account; +import im.conversations.android.transformer.MessageContentWrapper; import im.conversations.android.xmpp.model.axolotl.Encrypted; import im.conversations.android.xmpp.model.axolotl.Header; import im.conversations.android.xmpp.model.axolotl.Key; @@ -109,6 +111,27 @@ public class AxolotlService extends AbstractAccountService { return session; } + public boolean decryptEmptyMessage(final BareJid from, final Encrypted encrypted) { + Preconditions.checkArgument( + !encrypted.hasPayload(), "Use decryptToMessageContent to decrypt payload messages"); + try { + final var payload = decrypt(from, encrypted); + return !payload.hasPayload(); + } catch (final AxolotlDecryptionException e) { + return false; + } + } + + public MessageContentWrapper decryptToMessageContent( + final BareJid from, final Encrypted encrypted) { + Preconditions.checkArgument(encrypted.hasPayload()); + try { + return MessageContentWrapper.ofAxolotl(decrypt(from, encrypted)); + } catch (final AxolotlDecryptionException e) { + return MessageContentWrapper.ofAxolotlException(e); + } + } + public AxolotlPayload decrypt(final Jid from, final Encrypted encrypted) throws AxolotlDecryptionException { final AxolotlPayload axolotlPayload; @@ -150,10 +173,6 @@ public class AxolotlService extends AbstractAccountService { final Header header = encrypted.getHeader(); final Key ourKey = header.getKey(signalProtocolStore.getLocalRegistrationId()); if (ourKey == null) { - LOGGER.info( - "looking for {} in {}", - signalProtocolStore.getLocalRegistrationId(), - header.getKeys()); throw new NotEncryptedForThisDeviceException(); } final byte[] keyWithAuthTag; diff --git a/app/src/main/java/im/conversations/android/database/model/Encryption.java b/app/src/main/java/im/conversations/android/database/model/Encryption.java index 60f6f6244..ac4b7304e 100644 --- a/app/src/main/java/im/conversations/android/database/model/Encryption.java +++ b/app/src/main/java/im/conversations/android/database/model/Encryption.java @@ -2,6 +2,7 @@ package im.conversations.android.database.model; public enum Encryption { OMEMO, + FAILURE, CLEARTEXT, PGP } diff --git a/app/src/main/java/im/conversations/android/database/model/PartType.java b/app/src/main/java/im/conversations/android/database/model/PartType.java index 36d7c971a..496ec6326 100644 --- a/app/src/main/java/im/conversations/android/database/model/PartType.java +++ b/app/src/main/java/im/conversations/android/database/model/PartType.java @@ -7,5 +7,7 @@ public enum PartType { MODERATION, VIDEO_CALL, AUDIO_CALL, - MISSED_CALL + MISSED_CALL, + NOT_ENCRYPTED_FOR_THIS_DEVICE, + DECRYPTION_FAILURE } diff --git a/app/src/main/java/im/conversations/android/transformer/MessageContentWrapper.java b/app/src/main/java/im/conversations/android/transformer/MessageContentWrapper.java index 37166c717..777393d75 100644 --- a/app/src/main/java/im/conversations/android/transformer/MessageContentWrapper.java +++ b/app/src/main/java/im/conversations/android/transformer/MessageContentWrapper.java @@ -2,9 +2,12 @@ package im.conversations.android.transformer; import com.google.common.base.Preconditions; import com.google.common.base.Strings; +import com.google.common.base.Throwables; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; +import im.conversations.android.axolotl.AxolotlDecryptionException; import im.conversations.android.axolotl.AxolotlPayload; +import im.conversations.android.axolotl.NotEncryptedForThisDeviceException; import im.conversations.android.database.model.Encryption; import im.conversations.android.database.model.MessageContent; import im.conversations.android.database.model.PartType; @@ -23,6 +26,10 @@ public class MessageContentWrapper { Encryption.CLEARTEXT, null); + private static final List NOT_ENCRYPTED_FOR_THIS_DEVICE = + ImmutableList.of( + new MessageContent(null, PartType.NOT_ENCRYPTED_FOR_THIS_DEVICE, null, null)); + public final List contents; public final Encryption encryption; public final IdentityKey identityKey; @@ -86,6 +93,29 @@ public class MessageContentWrapper { String.format("%s does not have payload", payload.getClass().getSimpleName())); } + public static MessageContentWrapper ofAxolotlException(final AxolotlDecryptionException e) { + final Throwable cause = Throwables.getRootCause(e); + if (cause instanceof NotEncryptedForThisDeviceException) { + return new MessageContentWrapper( + NOT_ENCRYPTED_FOR_THIS_DEVICE, Encryption.FAILURE, null); + } else { + return new MessageContentWrapper( + ImmutableList.of( + new MessageContent( + null, + PartType.DECRYPTION_FAILURE, + exceptionToMessage(cause), + null)), + Encryption.FAILURE, + null); + } + } + + private static String exceptionToMessage(final Throwable throwable) { + final String message = throwable.getMessage(); + return message == null ? throwable.getClass().getSimpleName() : message; + } + public boolean isEmpty() { return this.contents.isEmpty(); } diff --git a/app/src/main/java/im/conversations/android/transformer/Transformer.java b/app/src/main/java/im/conversations/android/transformer/Transformer.java index 672aca560..82cc95814 100644 --- a/app/src/main/java/im/conversations/android/transformer/Transformer.java +++ b/app/src/main/java/im/conversations/android/transformer/Transformer.java @@ -4,7 +4,6 @@ import android.content.Context; import androidx.annotation.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; -import im.conversations.android.axolotl.AxolotlDecryptionException; import im.conversations.android.axolotl.AxolotlService; import im.conversations.android.database.ConversationsDatabase; import im.conversations.android.database.model.Account; @@ -131,18 +130,13 @@ public class Transformer { final Encrypted encrypted = transformation.getExtension(Encrypted.class); final MessageContentWrapper contents; if (encrypted != null) { - try { - final var payload = - axolotlService.decrypt(transformation.senderIdentity(), encrypted); - if (payload.hasPayload()) { - contents = MessageContentWrapper.ofAxolotl(payload); - } else { - return true; - } - } catch (final AxolotlDecryptionException e) { - LOGGER.error("Could not decrypt message", e); - // TODO if message had payload create error message entry - return false; + if (encrypted.hasPayload()) { + contents = + axolotlService.decryptToMessageContent( + transformation.senderIdentity(), encrypted); + } else { + return axolotlService.decryptEmptyMessage( + transformation.senderIdentity(), encrypted); } } else { // TODO we need to remove fallbacks for reactions, retractions and potentially other diff --git a/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java b/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java index f84f34018..b1e39e5b3 100644 --- a/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java +++ b/app/src/main/java/im/conversations/android/xmpp/manager/ArchiveManager.java @@ -97,8 +97,9 @@ public class ArchiveManager extends AbstractManager { final var transformation = this.transformationFactory.create( forwardedMessage, stanzaId, receivedAt, privilegedExtensionBuilder.build()); - // TODO only when there is something to transform - runningQuery.addTransformation(transformation); + if (transformation.isAnythingToTransform()) { + runningQuery.addTransformation(transformation); + } } private ListenableFuture fetchMetadata(final Jid archive) { diff --git a/app/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java b/app/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java index 4e48c0b83..5f5639583 100644 --- a/app/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java +++ b/app/src/main/java/im/conversations/android/xmpp/processor/MessageProcessor.java @@ -20,6 +20,7 @@ import im.conversations.android.xmpp.model.mam.Result; import im.conversations.android.xmpp.model.pubsub.event.Event; import im.conversations.android.xmpp.model.stanza.Message; import im.conversations.android.xmpp.model.state.ChatStateNotification; +import java.util.Arrays; import java.util.function.Consumer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -71,12 +72,6 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume return; } - LOGGER.debug( - "Message from {} with {} in level {}", - message.getFrom(), - message.getExtensionIds(), - this.level); - final var from = message.getFrom(); final var id = message.getId(); @@ -92,7 +87,9 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume } else { sendReceipts = true; } - if (sendReceipts) { + if (sendReceipts + && Arrays.asList(Message.Type.CHAT, Message.Type.NORMAL) + .contains(message.getType())) { getManager(ReceiptManager.class) .received(from, id, transformation.deliveryReceiptRequests); }