create stub message contents for encryption failures
This commit is contained in:
parent
ee1c938f2a
commit
7c820f7b32
|
@ -3,6 +3,7 @@ package im.conversations.android.axolotl;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import com.google.common.base.Optional;
|
import com.google.common.base.Optional;
|
||||||
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ArrayListMultimap;
|
import com.google.common.collect.ArrayListMultimap;
|
||||||
import com.google.common.collect.ImmutableMultimap;
|
import com.google.common.collect.ImmutableMultimap;
|
||||||
import com.google.common.collect.ImmutableSet;
|
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.AxolotlDatabaseStore;
|
||||||
import im.conversations.android.database.ConversationsDatabase;
|
import im.conversations.android.database.ConversationsDatabase;
|
||||||
import im.conversations.android.database.model.Account;
|
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.Encrypted;
|
||||||
import im.conversations.android.xmpp.model.axolotl.Header;
|
import im.conversations.android.xmpp.model.axolotl.Header;
|
||||||
import im.conversations.android.xmpp.model.axolotl.Key;
|
import im.conversations.android.xmpp.model.axolotl.Key;
|
||||||
|
@ -109,6 +111,27 @@ public class AxolotlService extends AbstractAccountService {
|
||||||
return session;
|
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)
|
public AxolotlPayload decrypt(final Jid from, final Encrypted encrypted)
|
||||||
throws AxolotlDecryptionException {
|
throws AxolotlDecryptionException {
|
||||||
final AxolotlPayload axolotlPayload;
|
final AxolotlPayload axolotlPayload;
|
||||||
|
@ -150,10 +173,6 @@ public class AxolotlService extends AbstractAccountService {
|
||||||
final Header header = encrypted.getHeader();
|
final Header header = encrypted.getHeader();
|
||||||
final Key ourKey = header.getKey(signalProtocolStore.getLocalRegistrationId());
|
final Key ourKey = header.getKey(signalProtocolStore.getLocalRegistrationId());
|
||||||
if (ourKey == null) {
|
if (ourKey == null) {
|
||||||
LOGGER.info(
|
|
||||||
"looking for {} in {}",
|
|
||||||
signalProtocolStore.getLocalRegistrationId(),
|
|
||||||
header.getKeys());
|
|
||||||
throw new NotEncryptedForThisDeviceException();
|
throw new NotEncryptedForThisDeviceException();
|
||||||
}
|
}
|
||||||
final byte[] keyWithAuthTag;
|
final byte[] keyWithAuthTag;
|
||||||
|
|
|
@ -2,6 +2,7 @@ package im.conversations.android.database.model;
|
||||||
|
|
||||||
public enum Encryption {
|
public enum Encryption {
|
||||||
OMEMO,
|
OMEMO,
|
||||||
|
FAILURE,
|
||||||
CLEARTEXT,
|
CLEARTEXT,
|
||||||
PGP
|
PGP
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,5 +7,7 @@ public enum PartType {
|
||||||
MODERATION,
|
MODERATION,
|
||||||
VIDEO_CALL,
|
VIDEO_CALL,
|
||||||
AUDIO_CALL,
|
AUDIO_CALL,
|
||||||
MISSED_CALL
|
MISSED_CALL,
|
||||||
|
NOT_ENCRYPTED_FOR_THIS_DEVICE,
|
||||||
|
DECRYPTION_FAILURE
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,12 @@ package im.conversations.android.transformer;
|
||||||
|
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.base.Strings;
|
import com.google.common.base.Strings;
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
|
import im.conversations.android.axolotl.AxolotlDecryptionException;
|
||||||
import im.conversations.android.axolotl.AxolotlPayload;
|
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.Encryption;
|
||||||
import im.conversations.android.database.model.MessageContent;
|
import im.conversations.android.database.model.MessageContent;
|
||||||
import im.conversations.android.database.model.PartType;
|
import im.conversations.android.database.model.PartType;
|
||||||
|
@ -23,6 +26,10 @@ public class MessageContentWrapper {
|
||||||
Encryption.CLEARTEXT,
|
Encryption.CLEARTEXT,
|
||||||
null);
|
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 List<MessageContent> contents;
|
||||||
public final Encryption encryption;
|
public final Encryption encryption;
|
||||||
public final IdentityKey identityKey;
|
public final IdentityKey identityKey;
|
||||||
|
@ -86,6 +93,29 @@ public class MessageContentWrapper {
|
||||||
String.format("%s does not have payload", payload.getClass().getSimpleName()));
|
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() {
|
public boolean isEmpty() {
|
||||||
return this.contents.isEmpty();
|
return this.contents.isEmpty();
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import android.content.Context;
|
||||||
import androidx.annotation.VisibleForTesting;
|
import androidx.annotation.VisibleForTesting;
|
||||||
import com.google.common.base.Preconditions;
|
import com.google.common.base.Preconditions;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import im.conversations.android.axolotl.AxolotlDecryptionException;
|
|
||||||
import im.conversations.android.axolotl.AxolotlService;
|
import im.conversations.android.axolotl.AxolotlService;
|
||||||
import im.conversations.android.database.ConversationsDatabase;
|
import im.conversations.android.database.ConversationsDatabase;
|
||||||
import im.conversations.android.database.model.Account;
|
import im.conversations.android.database.model.Account;
|
||||||
|
@ -131,18 +130,13 @@ public class Transformer {
|
||||||
final Encrypted encrypted = transformation.getExtension(Encrypted.class);
|
final Encrypted encrypted = transformation.getExtension(Encrypted.class);
|
||||||
final MessageContentWrapper contents;
|
final MessageContentWrapper contents;
|
||||||
if (encrypted != null) {
|
if (encrypted != null) {
|
||||||
try {
|
if (encrypted.hasPayload()) {
|
||||||
final var payload =
|
contents =
|
||||||
axolotlService.decrypt(transformation.senderIdentity(), encrypted);
|
axolotlService.decryptToMessageContent(
|
||||||
if (payload.hasPayload()) {
|
transformation.senderIdentity(), encrypted);
|
||||||
contents = MessageContentWrapper.ofAxolotl(payload);
|
} else {
|
||||||
} else {
|
return axolotlService.decryptEmptyMessage(
|
||||||
return true;
|
transformation.senderIdentity(), encrypted);
|
||||||
}
|
|
||||||
} catch (final AxolotlDecryptionException e) {
|
|
||||||
LOGGER.error("Could not decrypt message", e);
|
|
||||||
// TODO if message had payload create error message entry
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// TODO we need to remove fallbacks for reactions, retractions and potentially other
|
// TODO we need to remove fallbacks for reactions, retractions and potentially other
|
||||||
|
|
|
@ -97,8 +97,9 @@ public class ArchiveManager extends AbstractManager {
|
||||||
final var transformation =
|
final var transformation =
|
||||||
this.transformationFactory.create(
|
this.transformationFactory.create(
|
||||||
forwardedMessage, stanzaId, receivedAt, privilegedExtensionBuilder.build());
|
forwardedMessage, stanzaId, receivedAt, privilegedExtensionBuilder.build());
|
||||||
// TODO only when there is something to transform
|
if (transformation.isAnythingToTransform()) {
|
||||||
runningQuery.addTransformation(transformation);
|
runningQuery.addTransformation(transformation);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListenableFuture<Metadata> fetchMetadata(final Jid archive) {
|
private ListenableFuture<Metadata> fetchMetadata(final Jid archive) {
|
||||||
|
|
|
@ -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.pubsub.event.Event;
|
||||||
import im.conversations.android.xmpp.model.stanza.Message;
|
import im.conversations.android.xmpp.model.stanza.Message;
|
||||||
import im.conversations.android.xmpp.model.state.ChatStateNotification;
|
import im.conversations.android.xmpp.model.state.ChatStateNotification;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
@ -71,12 +72,6 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGGER.debug(
|
|
||||||
"Message from {} with {} in level {}",
|
|
||||||
message.getFrom(),
|
|
||||||
message.getExtensionIds(),
|
|
||||||
this.level);
|
|
||||||
|
|
||||||
final var from = message.getFrom();
|
final var from = message.getFrom();
|
||||||
|
|
||||||
final var id = message.getId();
|
final var id = message.getId();
|
||||||
|
@ -92,7 +87,9 @@ public class MessageProcessor extends XmppConnection.Delegate implements Consume
|
||||||
} else {
|
} else {
|
||||||
sendReceipts = true;
|
sendReceipts = true;
|
||||||
}
|
}
|
||||||
if (sendReceipts) {
|
if (sendReceipts
|
||||||
|
&& Arrays.asList(Message.Type.CHAT, Message.Type.NORMAL)
|
||||||
|
.contains(message.getType())) {
|
||||||
getManager(ReceiptManager.class)
|
getManager(ReceiptManager.class)
|
||||||
.received(from, id, transformation.deliveryReceiptRequests);
|
.received(from, id, transformation.deliveryReceiptRequests);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue