create stub message contents for encryption failures

This commit is contained in:
Daniel Gultsch 2023-03-11 15:56:17 +01:00
parent ee1c938f2a
commit 7c820f7b32
No known key found for this signature in database
GPG key ID: F43D18AD2A0982C2
7 changed files with 71 additions and 27 deletions

View file

@ -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;

View file

@ -2,6 +2,7 @@ package im.conversations.android.database.model;
public enum Encryption {
OMEMO,
FAILURE,
CLEARTEXT,
PGP
}

View file

@ -7,5 +7,7 @@ public enum PartType {
MODERATION,
VIDEO_CALL,
AUDIO_CALL,
MISSED_CALL
MISSED_CALL,
NOT_ENCRYPTED_FOR_THIS_DEVICE,
DECRYPTION_FAILURE
}

View file

@ -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<MessageContent> NOT_ENCRYPTED_FOR_THIS_DEVICE =
ImmutableList.of(
new MessageContent(null, PartType.NOT_ENCRYPTED_FOR_THIS_DEVICE, null, null));
public final List<MessageContent> 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();
}

View file

@ -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);
if (encrypted.hasPayload()) {
contents =
axolotlService.decryptToMessageContent(
transformation.senderIdentity(), encrypted);
} 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;
return axolotlService.decryptEmptyMessage(
transformation.senderIdentity(), encrypted);
}
} else {
// TODO we need to remove fallbacks for reactions, retractions and potentially other

View file

@ -97,9 +97,10 @@ public class ArchiveManager extends AbstractManager {
final var transformation =
this.transformationFactory.create(
forwardedMessage, stanzaId, receivedAt, privilegedExtensionBuilder.build());
// TODO only when there is something to transform
if (transformation.isAnythingToTransform()) {
runningQuery.addTransformation(transformation);
}
}
private ListenableFuture<Metadata> fetchMetadata(final Jid archive) {
final var iq = new Iq(Iq.Type.GET);

View file

@ -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);
}