mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Request message resend if incoming message can't be decrypted
This commit is contained in:
parent
fbafa75fe2
commit
62d8873a92
7 changed files with 158 additions and 6 deletions
|
@ -239,6 +239,7 @@ public class Manager implements Closeable {
|
|||
contactHelper,
|
||||
attachmentHelper,
|
||||
syncHelper,
|
||||
this::getRecipientProfile,
|
||||
jobExecutor);
|
||||
}
|
||||
|
||||
|
@ -876,11 +877,11 @@ public class Manager implements Closeable {
|
|||
// store message on disk, before acknowledging receipt to the server
|
||||
cachedMessage[0] = account.getMessageCache().cacheMessage(envelope1, recipientId);
|
||||
});
|
||||
logger.debug("New message received from server");
|
||||
if (result.isPresent()) {
|
||||
envelope = result.get();
|
||||
logger.debug("New message received from server");
|
||||
} else {
|
||||
// Received indicator that server queue is empty
|
||||
logger.debug("Received indicator that server queue is empty");
|
||||
handleQueuedActions(queuedActions);
|
||||
queuedActions.clear();
|
||||
|
||||
|
|
|
@ -0,0 +1,89 @@
|
|||
package org.asamk.signal.manager.actions;
|
||||
|
||||
import org.asamk.signal.manager.groups.GroupId;
|
||||
import org.asamk.signal.manager.jobs.Context;
|
||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||
import org.signal.libsignal.metadata.ProtocolException;
|
||||
import org.whispersystems.libsignal.protocol.CiphertextMessage;
|
||||
import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
|
||||
import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
|
||||
|
||||
public class SendRetryMessageRequestAction implements HandleAction {
|
||||
|
||||
private final RecipientId recipientId;
|
||||
private final ProtocolException protocolException;
|
||||
private final SignalServiceEnvelope envelope;
|
||||
|
||||
public SendRetryMessageRequestAction(
|
||||
final RecipientId recipientId,
|
||||
final ProtocolException protocolException,
|
||||
final SignalServiceEnvelope envelope
|
||||
) {
|
||||
this.recipientId = recipientId;
|
||||
this.protocolException = protocolException;
|
||||
this.envelope = envelope;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(Context context) throws Throwable {
|
||||
context.getAccount().getSessionStore().archiveSessions(recipientId);
|
||||
|
||||
int senderDevice = protocolException.getSenderDevice();
|
||||
Optional<GroupId> groupId = protocolException.getGroupId().isPresent() ? Optional.of(GroupId.unknownVersion(
|
||||
protocolException.getGroupId().get())) : Optional.absent();
|
||||
|
||||
byte[] originalContent;
|
||||
int envelopeType;
|
||||
if (protocolException.getUnidentifiedSenderMessageContent().isPresent()) {
|
||||
final var messageContent = protocolException.getUnidentifiedSenderMessageContent().get();
|
||||
originalContent = messageContent.getContent();
|
||||
envelopeType = messageContent.getType();
|
||||
} else {
|
||||
originalContent = envelope.getContent();
|
||||
envelopeType = envelopeTypeToCiphertextMessageType(envelope.getType());
|
||||
}
|
||||
|
||||
DecryptionErrorMessage decryptionErrorMessage = DecryptionErrorMessage.forOriginalMessage(originalContent,
|
||||
envelopeType,
|
||||
envelope.getTimestamp(),
|
||||
senderDevice);
|
||||
|
||||
context.getSendHelper().sendRetryReceipt(decryptionErrorMessage, recipientId, groupId);
|
||||
}
|
||||
|
||||
private static int envelopeTypeToCiphertextMessageType(int envelopeType) {
|
||||
switch (envelopeType) {
|
||||
case SignalServiceProtos.Envelope.Type.PREKEY_BUNDLE_VALUE:
|
||||
return CiphertextMessage.PREKEY_TYPE;
|
||||
case SignalServiceProtos.Envelope.Type.UNIDENTIFIED_SENDER_VALUE:
|
||||
return CiphertextMessage.SENDERKEY_TYPE;
|
||||
case SignalServiceProtos.Envelope.Type.PLAINTEXT_CONTENT_VALUE:
|
||||
return CiphertextMessage.PLAINTEXT_CONTENT_TYPE;
|
||||
case SignalServiceProtos.Envelope.Type.CIPHERTEXT_VALUE:
|
||||
default:
|
||||
return CiphertextMessage.WHISPER_TYPE;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
if (o == null || getClass() != o.getClass()) return false;
|
||||
|
||||
final SendRetryMessageRequestAction that = (SendRetryMessageRequestAction) o;
|
||||
|
||||
if (!recipientId.equals(that.recipientId)) return false;
|
||||
if (!protocolException.equals(that.protocolException)) return false;
|
||||
return envelope.equals(that.envelope);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = recipientId.hashCode();
|
||||
result = 31 * result + protocolException.hashCode();
|
||||
result = 31 * result + envelope.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -13,6 +13,7 @@ import org.asamk.signal.manager.actions.RetrieveStorageDataAction;
|
|||
import org.asamk.signal.manager.actions.SendGroupInfoAction;
|
||||
import org.asamk.signal.manager.actions.SendGroupInfoRequestAction;
|
||||
import org.asamk.signal.manager.actions.SendReceiptAction;
|
||||
import org.asamk.signal.manager.actions.SendRetryMessageRequestAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncBlockedListAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncContactsAction;
|
||||
import org.asamk.signal.manager.actions.SendSyncGroupsAction;
|
||||
|
@ -23,12 +24,17 @@ import org.asamk.signal.manager.groups.GroupUtils;
|
|||
import org.asamk.signal.manager.jobs.RetrieveStickerPackJob;
|
||||
import org.asamk.signal.manager.storage.SignalAccount;
|
||||
import org.asamk.signal.manager.storage.groups.GroupInfoV1;
|
||||
import org.asamk.signal.manager.storage.recipients.Profile;
|
||||
import org.asamk.signal.manager.storage.recipients.RecipientId;
|
||||
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
||||
import org.asamk.signal.manager.storage.stickers.Sticker;
|
||||
import org.asamk.signal.manager.storage.stickers.StickerPackId;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidKeyIdException;
|
||||
import org.signal.libsignal.metadata.ProtocolInvalidMessageException;
|
||||
import org.signal.libsignal.metadata.ProtocolNoSessionException;
|
||||
import org.signal.libsignal.metadata.ProtocolUntrustedIdentityException;
|
||||
import org.signal.libsignal.metadata.SelfSendException;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.profiles.ProfileKey;
|
||||
import org.slf4j.Logger;
|
||||
|
@ -59,6 +65,7 @@ public final class IncomingMessageHandler {
|
|||
private final ContactHelper contactHelper;
|
||||
private final AttachmentHelper attachmentHelper;
|
||||
private final SyncHelper syncHelper;
|
||||
private final ProfileProvider profileProvider;
|
||||
private final JobExecutor jobExecutor;
|
||||
|
||||
public IncomingMessageHandler(
|
||||
|
@ -70,6 +77,7 @@ public final class IncomingMessageHandler {
|
|||
final ContactHelper contactHelper,
|
||||
final AttachmentHelper attachmentHelper,
|
||||
final SyncHelper syncHelper,
|
||||
final ProfileProvider profileProvider,
|
||||
final JobExecutor jobExecutor
|
||||
) {
|
||||
this.account = account;
|
||||
|
@ -80,6 +88,7 @@ public final class IncomingMessageHandler {
|
|||
this.contactHelper = contactHelper;
|
||||
this.attachmentHelper = attachmentHelper;
|
||||
this.syncHelper = syncHelper;
|
||||
this.profileProvider = profileProvider;
|
||||
this.jobExecutor = jobExecutor;
|
||||
}
|
||||
|
||||
|
@ -131,11 +140,24 @@ public final class IncomingMessageHandler {
|
|||
actions.add(new RetrieveProfileAction(recipientId));
|
||||
exception = new UntrustedIdentityException(addressResolver.resolveSignalServiceAddress(recipientId),
|
||||
e.getSenderDevice());
|
||||
} catch (ProtocolInvalidMessageException e) {
|
||||
} catch (ProtocolInvalidKeyIdException | ProtocolInvalidKeyException | ProtocolNoSessionException | ProtocolInvalidMessageException e) {
|
||||
final var sender = account.getRecipientStore().resolveRecipient(e.getSender());
|
||||
final var senderProfile = profileProvider.getProfile(sender);
|
||||
final var selfProfile = profileProvider.getProfile(account.getSelfRecipientId());
|
||||
if (senderProfile != null
|
||||
&& senderProfile.getCapabilities().contains(Profile.Capability.senderKey)
|
||||
&& selfProfile != null
|
||||
&& selfProfile.getCapabilities().contains(Profile.Capability.senderKey)) {
|
||||
logger.debug("Received invalid message, requesting message resend.");
|
||||
actions.add(new SendRetryMessageRequestAction(sender, e, envelope));
|
||||
} else {
|
||||
logger.debug("Received invalid message, queuing renew session action.");
|
||||
actions.add(new RenewSessionAction(sender));
|
||||
}
|
||||
exception = e;
|
||||
} catch (SelfSendException e) {
|
||||
logger.debug("Dropping unidentified message from self.");
|
||||
return new Pair<>(List.of(), null);
|
||||
} catch (Exception e) {
|
||||
exception = e;
|
||||
}
|
||||
|
|
|
@ -5,5 +5,5 @@ import org.asamk.signal.manager.storage.recipients.RecipientId;
|
|||
|
||||
public interface ProfileProvider {
|
||||
|
||||
Profile getProfile(RecipientId address);
|
||||
Profile getProfile(RecipientId recipientId);
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@ import org.asamk.signal.manager.storage.recipients.RecipientId;
|
|||
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
|
||||
import org.whispersystems.libsignal.util.guava.Optional;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageSender;
|
||||
import org.whispersystems.signalservice.api.crypto.ContentHint;
|
||||
|
@ -156,6 +157,25 @@ public class SendHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public void sendRetryReceipt(
|
||||
DecryptionErrorMessage errorMessage, RecipientId recipientId, Optional<GroupId> groupId
|
||||
) throws IOException, UntrustedIdentityException {
|
||||
var messageSender = dependencies.getMessageSender();
|
||||
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
|
||||
logger.debug("Sending retry receipt for {} to {}, device: {}",
|
||||
errorMessage.getTimestamp(),
|
||||
recipientId,
|
||||
errorMessage.getDeviceId());
|
||||
try {
|
||||
messageSender.sendRetryReceipt(address,
|
||||
unidentifiedAccessHelper.getAccessFor(recipientId),
|
||||
groupId.transform(GroupId::serialize),
|
||||
errorMessage);
|
||||
} catch (org.whispersystems.signalservice.api.crypto.UntrustedIdentityException e) {
|
||||
throw new UntrustedIdentityException(address);
|
||||
}
|
||||
}
|
||||
|
||||
public SendMessageResult sendNullMessage(RecipientId recipientId) throws IOException {
|
||||
var messageSender = dependencies.getMessageSender();
|
||||
|
||||
|
|
|
@ -16,6 +16,11 @@ public class RecipientId {
|
|||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RecipientId{" + "id=" + id + '}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(final Object o) {
|
||||
if (this == o) return true;
|
||||
|
|
|
@ -8,6 +8,7 @@ import org.asamk.signal.manager.groups.GroupUtils;
|
|||
import org.asamk.signal.util.DateUtils;
|
||||
import org.asamk.signal.util.Util;
|
||||
import org.slf4j.helpers.MessageFormatter;
|
||||
import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceContent;
|
||||
import org.whispersystems.signalservice.api.messages.SignalServiceDataMessage;
|
||||
|
@ -113,6 +114,11 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
|||
var typingMessage = content.getTypingMessage().get();
|
||||
printTypingMessage(writer.indentedWriter(), typingMessage);
|
||||
}
|
||||
if (content.getDecryptionErrorMessage().isPresent()) {
|
||||
writer.println("Received a decryption error message (resend request)");
|
||||
var decryptionErrorMessage = content.getDecryptionErrorMessage().get();
|
||||
printDecryptionErrorMessage(writer.indentedWriter(), decryptionErrorMessage);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
writer.println("Unknown message received.");
|
||||
|
@ -215,6 +221,15 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
|
|||
}
|
||||
}
|
||||
|
||||
private void printDecryptionErrorMessage(
|
||||
final PlainTextWriter writer, final DecryptionErrorMessage decryptionErrorMessage
|
||||
) {
|
||||
writer.println("Device id: {}", decryptionErrorMessage.getDeviceId());
|
||||
writer.println("Timestamp: {}", DateUtils.formatTimestamp(decryptionErrorMessage.getTimestamp()));
|
||||
writer.println("Ratchet key: {}",
|
||||
decryptionErrorMessage.getRatchetKey().isPresent() ? "is present" : "not present");
|
||||
}
|
||||
|
||||
private void printReceiptMessage(
|
||||
final PlainTextWriter writer, final SignalServiceReceiptMessage receiptMessage
|
||||
) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue