From 173983a1aba191549d10e07ff36dd61fe7b72715 Mon Sep 17 00:00:00 2001 From: gram-signal <84339875+gram-signal@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:46:36 -0700 Subject: [PATCH] Use RemoteConfig for UsePqRatchet. Co-authored-by: Greyson Parrelli --- .../org/thoughtcrime/securesms/testing/BobClient.kt | 5 +++-- .../dependencies/ApplicationDependencyProvider.java | 3 ++- .../securesms/messages/MessageDecryptor.kt | 2 +- .../org/thoughtcrime/securesms/util/RemoteConfig.kt | 11 +++++++++++ .../signalservice/api/SignalServiceMessageSender.java | 10 +++++++--- .../signalservice/api/crypto/SignalServiceCipher.java | 9 +++++---- .../api/crypto/SignalSessionBuilder.java | 4 ++-- .../signalservice/api/crypto/SignalSessionCipher.java | 4 ++-- .../androidTest/java/org/signal/util/SignalClient.kt | 2 +- 9 files changed, 34 insertions(+), 16 deletions(-) diff --git a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt index 237f176d9c..7e62467369 100644 --- a/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt +++ b/app/src/androidTest/java/org/thoughtcrime/securesms/testing/BobClient.kt @@ -6,6 +6,7 @@ import org.signal.libsignal.protocol.IdentityKey import org.signal.libsignal.protocol.IdentityKeyPair import org.signal.libsignal.protocol.SessionBuilder import org.signal.libsignal.protocol.SignalProtocolAddress +import org.signal.libsignal.protocol.UsePqRatchet import org.signal.libsignal.protocol.ecc.ECKeyPair import org.signal.libsignal.protocol.groups.state.SenderKeyRecord import org.signal.libsignal.protocol.state.IdentityKeyStore @@ -68,7 +69,7 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: if (!aciStore.containsSession(getAliceProtocolAddress())) { val sessionBuilder = SignalSessionBuilder(sessionLock, SessionBuilder(aciStore, getAliceProtocolAddress())) - sessionBuilder.process(getAlicePreKeyBundle()) + sessionBuilder.process(getAlicePreKeyBundle(), UsePqRatchet.NO) } return cipher.encrypt(getAliceProtocolAddress(), getAliceUnidentifiedAccess(), envelopeContent) @@ -77,7 +78,7 @@ class BobClient(val serviceId: ServiceId, val e164: String, val identityKeyPair: fun decrypt(envelope: Envelope, serverDeliveredTimestamp: Long) { val cipher = SignalServiceCipher(serviceAddress, 1, aciStore, sessionLock, SealedSenderAccessUtil.getCertificateValidator()) - cipher.decrypt(envelope, serverDeliveredTimestamp) + cipher.decrypt(envelope, serverDeliveredTimestamp, UsePqRatchet.NO) } private fun getAliceServiceId(): ServiceId { diff --git a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java index 28481afdf9..4815a78e04 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java +++ b/app/src/main/java/org/thoughtcrime/securesms/dependencies/ApplicationDependencyProvider.java @@ -170,7 +170,8 @@ public class ApplicationDependencyProvider implements AppDependencies.Provider { Optional.of(new SecurityEventListener(context)), SignalExecutors.newCachedBoundedExecutor("signal-messages", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD, 1, 16, 30), ByteUnit.KILOBYTES.toBytes(256), - RemoteConfig::useMessageSendRestFallback); + RemoteConfig::useMessageSendRestFallback, + RemoteConfig.usePqRatchet()); } @Override diff --git a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt index c5f3ff8b11..7c5033cc0f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/messages/MessageDecryptor.kt @@ -142,7 +142,7 @@ object MessageDecryptor { return try { val startTimeNanos = System.nanoTime() - val cipherResult: SignalServiceCipherResult? = cipher.decrypt(envelope, serverDeliveredTimestamp) + val cipherResult: SignalServiceCipherResult? = cipher.decrypt(envelope, serverDeliveredTimestamp, RemoteConfig.usePqRatchet) val endTimeNanos = System.nanoTime() if (cipherResult == null) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt index 9a294270bd..61c7e34e85 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/util/RemoteConfig.kt @@ -11,6 +11,7 @@ import org.signal.core.util.gibiBytes import org.signal.core.util.kibiBytes import org.signal.core.util.logging.Log import org.signal.core.util.mebiBytes +import org.signal.libsignal.protocol.UsePqRatchet import org.thoughtcrime.securesms.BuildConfig import org.thoughtcrime.securesms.dependencies.AppDependencies import org.thoughtcrime.securesms.groups.SelectionLimits @@ -1154,5 +1155,15 @@ object RemoteConfig { durationUnit = DurationUnit.DAYS ) + /** Whether or not to use the new post-quantum ratcheting. */ + @JvmStatic + @get:JvmName("usePqRatchet") + val usePqRatchet: UsePqRatchet by remoteValue( + key = "global.usePqRatchet", + hotSwappable = true + ) { value -> + if (value.asBoolean(false)) UsePqRatchet.YES else UsePqRatchet.NO + } + // endregion } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java index 9df15bd44a..c25c6b65bd 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/SignalServiceMessageSender.java @@ -13,6 +13,7 @@ import org.signal.libsignal.protocol.InvalidRegistrationIdException; import org.signal.libsignal.protocol.NoSessionException; import org.signal.libsignal.protocol.SessionBuilder; import org.signal.libsignal.protocol.SignalProtocolAddress; +import org.signal.libsignal.protocol.UsePqRatchet; import org.signal.libsignal.protocol.groups.GroupSessionBuilder; import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.protocol.message.DecryptionErrorMessage; @@ -182,6 +183,7 @@ public class SignalServiceMessageSender { private final Scheduler scheduler; private final long maxEnvelopeSize; private final BooleanSupplier useRestFallback; + private final UsePqRatchet usePqRatchet; public SignalServiceMessageSender(PushServiceSocket pushServiceSocket, SignalServiceDataStore store, @@ -192,7 +194,8 @@ public class SignalServiceMessageSender { Optional eventListener, ExecutorService executor, long maxEnvelopeSize, - BooleanSupplier useRestFallback) + BooleanSupplier useRestFallback, + UsePqRatchet usePqRatchet) { CredentialsProvider credentialsProvider = pushServiceSocket.getCredentialsProvider(); @@ -210,6 +213,7 @@ public class SignalServiceMessageSender { this.scheduler = Schedulers.from(executor, false, false); this.keysApi = keysApi; this.useRestFallback = useRestFallback; + this.usePqRatchet = usePqRatchet; } /** @@ -2701,7 +2705,7 @@ public class SignalServiceMessageSender { try { SignalProtocolAddress preKeyAddress = new SignalProtocolAddress(recipient.getIdentifier(), preKey.getDeviceId()); SignalSessionBuilder sessionBuilder = new SignalSessionBuilder(sessionLock, new SessionBuilder(aciStore, preKeyAddress)); - sessionBuilder.process(preKey); + sessionBuilder.process(preKey, usePqRatchet); } catch (org.signal.libsignal.protocol.UntrustedIdentityException e) { throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey()); } @@ -2753,7 +2757,7 @@ public class SignalServiceMessageSender { try { SignalSessionBuilder sessionBuilder = new SignalSessionBuilder(sessionLock, new SessionBuilder(aciStore, new SignalProtocolAddress(recipient.getIdentifier(), missingDeviceId))); - sessionBuilder.process(preKey); + sessionBuilder.process(preKey, usePqRatchet); } catch (org.signal.libsignal.protocol.UntrustedIdentityException e) { throw new UntrustedIdentityException("Untrusted identity key!", recipient.getIdentifier(), preKey.getIdentityKey()); } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java index f24a057201..8ea64d9b39 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalServiceCipher.java @@ -34,6 +34,7 @@ import org.signal.libsignal.protocol.NoSessionException; import org.signal.libsignal.protocol.SessionCipher; import org.signal.libsignal.protocol.SignalProtocolAddress; import org.signal.libsignal.protocol.UntrustedIdentityException; +import org.signal.libsignal.protocol.UsePqRatchet; import org.signal.libsignal.protocol.groups.GroupCipher; import org.signal.libsignal.protocol.logging.Log; import org.signal.libsignal.protocol.message.CiphertextMessage; @@ -131,7 +132,7 @@ public class SignalServiceCipher { } } - public SignalServiceCipherResult decrypt(Envelope envelope, long serverDeliveredTimestamp) + public SignalServiceCipherResult decrypt(Envelope envelope, long serverDeliveredTimestamp, UsePqRatchet usePqRatchet) throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolInvalidKeyIdException, ProtocolLegacyMessageException, ProtocolUntrustedIdentityException, ProtocolNoSessionException, @@ -141,7 +142,7 @@ public class SignalServiceCipher { { try { if (envelope.content != null) { - Plaintext plaintext = decryptInternal(envelope, serverDeliveredTimestamp); + Plaintext plaintext = decryptInternal(envelope, serverDeliveredTimestamp, usePqRatchet); Content content = Content.ADAPTER.decode(plaintext.getData()); return new SignalServiceCipherResult( @@ -163,7 +164,7 @@ public class SignalServiceCipher { } } - private Plaintext decryptInternal(Envelope envelope, long serverDeliveredTimestamp) + private Plaintext decryptInternal(Envelope envelope, long serverDeliveredTimestamp, UsePqRatchet usePqRatchet) throws InvalidMetadataMessageException, InvalidMetadataVersionException, ProtocolDuplicateMessageException, ProtocolUntrustedIdentityException, ProtocolLegacyMessageException, ProtocolInvalidKeyException, @@ -184,7 +185,7 @@ public class SignalServiceCipher { SignalProtocolAddress sourceAddress = new SignalProtocolAddress(envelope.sourceServiceId, envelope.sourceDevice); SignalSessionCipher sessionCipher = new SignalSessionCipher(sessionLock, new SessionCipher(signalProtocolStore, sourceAddress)); - paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray())); + paddedMessage = sessionCipher.decrypt(new PreKeySignalMessage(envelope.content.toByteArray()), usePqRatchet); metadata = new SignalServiceMetadata(getSourceAddress(envelope), envelope.sourceDevice, envelope.timestamp, envelope.serverTimestamp, serverDeliveredTimestamp, false, envelope.serverGuid, Optional.empty(), envelope.destinationServiceId); signalProtocolStore.clearSenderKeySharedWith(Collections.singleton(sourceAddress)); diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionBuilder.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionBuilder.java index a2f3107c26..7b81fa3ddf 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionBuilder.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionBuilder.java @@ -20,9 +20,9 @@ public class SignalSessionBuilder { this.builder = builder; } - public void process(PreKeyBundle preKey) throws InvalidKeyException, UntrustedIdentityException { + public void process(PreKeyBundle preKey, UsePqRatchet usePqRatchet) throws InvalidKeyException, UntrustedIdentityException { try (SignalSessionLock.Lock unused = lock.acquire()) { - builder.process(preKey, UsePqRatchet.NO); + builder.process(preKey, usePqRatchet); } } } diff --git a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionCipher.java b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionCipher.java index 12a7129798..e02de4b205 100644 --- a/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionCipher.java +++ b/libsignal-service/src/main/java/org/whispersystems/signalservice/api/crypto/SignalSessionCipher.java @@ -34,9 +34,9 @@ public class SignalSessionCipher { } } - public byte[] decrypt(PreKeySignalMessage ciphertext) throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, InvalidKeyIdException, InvalidKeyException, org.signal.libsignal.protocol.UntrustedIdentityException { + public byte[] decrypt(PreKeySignalMessage ciphertext, UsePqRatchet usePqRatchet) throws DuplicateMessageException, LegacyMessageException, InvalidMessageException, InvalidKeyIdException, InvalidKeyException, org.signal.libsignal.protocol.UntrustedIdentityException { try (SignalSessionLock.Lock unused = lock.acquire()) { - return cipher.decrypt(ciphertext, UsePqRatchet.NO); + return cipher.decrypt(ciphertext, usePqRatchet); } } diff --git a/microbenchmark/src/androidTest/java/org/signal/util/SignalClient.kt b/microbenchmark/src/androidTest/java/org/signal/util/SignalClient.kt index 09e3317bbc..2a9a39bc28 100644 --- a/microbenchmark/src/androidTest/java/org/signal/util/SignalClient.kt +++ b/microbenchmark/src/androidTest/java/org/signal/util/SignalClient.kt @@ -163,7 +163,7 @@ class SignalClient { } fun decryptMessage(envelope: Envelope) { - cipher.decrypt(envelope, System.currentTimeMillis()) + cipher.decrypt(envelope, System.currentTimeMillis(), UsePqRatchet.NO) } private fun createPreKeyBundle(): PreKeyBundle {