diff --git a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java index 07bf017060..38f9366335 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java +++ b/app/src/main/java/org/thoughtcrime/securesms/jobs/PushSendJob.java @@ -58,9 +58,9 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil; import org.thoughtcrime.securesms.transport.RetryLaterException; import org.thoughtcrime.securesms.transport.UndeliverableMessageException; import org.thoughtcrime.securesms.util.MediaUtil; +import org.whispersystems.signalservice.api.InvalidPreKeyException; import org.whispersystems.signalservice.api.crypto.AttachmentCipherStreamUtil; import org.whispersystems.signalservice.api.messages.AttachmentTransferProgress; -import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentPointer; import org.whispersystems.signalservice.api.messages.SignalServiceAttachmentRemoteId; @@ -69,6 +69,7 @@ import org.whispersystems.signalservice.api.messages.SignalServicePreview; import org.whispersystems.signalservice.api.messages.shared.SharedContact; import org.whispersystems.signalservice.api.push.exceptions.ProofRequiredException; import org.whispersystems.signalservice.api.push.exceptions.ServerRejectedException; +import org.whispersystems.signalservice.internal.crypto.PaddingInputStream; import org.whispersystems.signalservice.internal.push.BodyRange; import org.whispersystems.signalservice.internal.push.http.ResumableUploadSpec; @@ -150,6 +151,10 @@ public abstract class PushSendJob extends SendJob { return false; } + if (exception instanceof InvalidPreKeyException) { + return false; + } + return exception instanceof IOException || exception instanceof RetryLaterException || exception instanceof ProofRequiredException; diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index bb7f67d9a1..e61b283325 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -2103,7 +2103,11 @@ public class SignalServiceMessageSender { } } catch (MismatchedDevicesException mde) { Log.w(TAG, "[sendMessage][" + timestamp + "] Handling mismatched devices. (" + mde.getMessage() + ")"); - handleMismatchedDevices(recipient, mde.getMismatchedDevices()); + try { + handleMismatchedDevices(recipient, mde.getMismatchedDevices()); + } catch (InvalidPreKeyException e) { + return SendMessageResult.invalidPreKeyFailure(recipient); + } } catch (StaleDevicesException ste) { Log.w(TAG, "[sendMessage][" + timestamp + "] Handling stale devices. (" + ste.getMessage() + ")"); handleStaleDevices(recipient, ste.getStaleDevices()); @@ -2580,15 +2584,39 @@ public class SignalServiceMessageSender { if (error instanceof MismatchedDeviceException) { MismatchedDeviceException mismatchedDeviceException = (MismatchedDeviceException) error; Log.w(TAG, "[sendGroupMessage][" + timestamp + "] Handling mismatched devices. (" + mismatchedDeviceException.getMessage() + ")"); + List invalidPreKeyResults = new LinkedList<>(); + for (MismatchedDeviceException.Entry entry : mismatchedDeviceException.getEntries()) { SignalServiceAddress address = new SignalServiceAddress(ServiceId.fromLibSignal(entry.getAccount())); MismatchedDevices devices = MismatchedDevices.fromLibSignal(entry); - handleMismatchedDevices(address, devices); + try { + handleMismatchedDevices(address, devices); + } catch (InvalidPreKeyException e) { + Log.w(TAG, "[sendGroupMessage][" + timestamp + "] Invalid prekey for " + address.getIdentifier() + " during mismatch handling."); + invalidPreKeyResults.add(SendMessageResult.invalidPreKeyFailure(address)); + continue; + } if (entry.getStaleDevices().length > 0) { StaleDevices staleDevices = StaleDevices.fromLibSignal(entry); handleStaleDevices(address, staleDevices); } } + + if (!invalidPreKeyResults.isEmpty()) { + Set failedAddresses = invalidPreKeyResults.stream() + .map(r -> r.getAddress().getServiceId()) + .collect(Collectors.toSet()); + + List networkFailures = recipients.stream() + .filter(r -> !failedAddresses.contains(r.getServiceId())) + .map(SendMessageResult::networkFailure) + .collect(Collectors.toList()); + + List combinedResults = new LinkedList<>(); + combinedResults.addAll(invalidPreKeyResults); + combinedResults.addAll(networkFailures); + return combinedResults; + } } else if (error instanceof RequestUnauthorizedException) { Log.w(TAG, "[sendGroupMessage][" + timestamp + "] Invalid access header."); throw new InvalidUnidentifiedAccessHeaderException(); diff --git a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt index 1a22dce208..0fd7488e8b 100644 --- a/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt +++ b/lib/libsignal-service/src/main/java/org/whispersystems/signalservice/api/keys/KeysApi.kt @@ -8,10 +8,13 @@ package org.whispersystems.signalservice.api.keys import org.signal.core.util.logging.Log import org.signal.core.util.toByteArray import org.signal.libsignal.protocol.IdentityKey +import org.signal.libsignal.protocol.InvalidKeyException +import org.signal.libsignal.protocol.SignalProtocolAddress import org.signal.libsignal.protocol.ecc.ECPublicKey import org.signal.libsignal.protocol.kem.KEMPublicKey import org.signal.libsignal.protocol.state.PreKeyBundle import org.signal.libsignal.protocol.state.PreKeyRecord +import org.whispersystems.signalservice.api.InvalidPreKeyException import org.whispersystems.signalservice.api.NetworkResult import org.whispersystems.signalservice.api.account.PreKeyUpload import org.whispersystems.signalservice.api.crypto.SealedSenderAccess @@ -29,7 +32,6 @@ import org.whispersystems.signalservice.internal.push.PreKeyResponse import org.whispersystems.signalservice.internal.push.PreKeyState import org.whispersystems.signalservice.internal.put import org.whispersystems.signalservice.internal.websocket.WebSocketRequestMessage -import java.io.IOException import java.security.MessageDigest import java.util.LinkedList @@ -169,7 +171,8 @@ class KeysApi( if (bundles.isNotEmpty()) { NetworkResult.Success(bundles[0]) } else { - NetworkResult.NetworkError(IOException("No prekeys available!")) + val address = SignalProtocolAddress(destination.identifier, deviceId) + NetworkResult.NetworkError(InvalidPreKeyException(address, InvalidKeyException("No valid prekeys available for $address"))) } } } @@ -216,7 +219,7 @@ class KeysApi( if (device.getSignedPreKey() != null) { val rawSignedPreKeyId = device.getSignedPreKey().keyId - if (rawSignedPreKeyId !in 0..Int.MAX_VALUE) { + if (rawSignedPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Signed pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue } @@ -230,7 +233,7 @@ class KeysApi( if (device.getPreKey() != null) { val rawPreKeyId = device.getPreKey().keyId - if (rawPreKeyId !in 0..Int.MAX_VALUE) { + if (rawPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue } @@ -240,7 +243,7 @@ class KeysApi( if (device.getKyberPreKey() != null) { val rawKyberPreKeyId = device.getKyberPreKey().keyId - if (rawKyberPreKeyId !in 0..Int.MAX_VALUE) { + if (rawKyberPreKeyId !in 0..Int.MAX_VALUE.toLong()) { Log.w(TAG, "Kyber pre-key ID for device ${device.deviceId} is out of valid range! Skipping.") continue }