]> nmode's Git Repositories - signal-cli/commitdiff
Key tables on serviceId instead of recipientId
authorAsamK <asamk@gmx.de>
Tue, 23 Aug 2022 12:17:14 +0000 (14:17 +0200)
committerAsamK <asamk@gmx.de>
Sun, 28 Aug 2022 14:04:05 +0000 (16:04 +0200)
25 files changed:
lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java
lib/src/main/java/org/asamk/signal/manager/actions/RenewSessionAction.java
lib/src/main/java/org/asamk/signal/manager/actions/SendRetryMessageRequestAction.java
lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/IncomingMessageHandler.java
lib/src/main/java/org/asamk/signal/manager/helper/ProfileHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java
lib/src/main/java/org/asamk/signal/manager/helper/SyncHelper.java
lib/src/main/java/org/asamk/signal/manager/storage/AccountDatabase.java
lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityInfo.java
lib/src/main/java/org/asamk/signal/manager/storage/identities/IdentityKeyStore.java
lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java
lib/src/main/java/org/asamk/signal/manager/storage/identities/SignalIdentityKeyStore.java
lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java
lib/src/main/java/org/asamk/signal/manager/storage/sendLog/MessageSendLogStore.java
lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/LegacySenderKeyRecordStore.java
lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/LegacySenderKeySharedStore.java
lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyRecordStore.java
lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeySharedStore.java
lib/src/main/java/org/asamk/signal/manager/storage/senderKeys/SenderKeyStore.java
lib/src/main/java/org/asamk/signal/manager/storage/sessions/LegacySessionStore.java
lib/src/main/java/org/asamk/signal/manager/storage/sessions/SessionStore.java
lib/src/main/java/org/asamk/signal/manager/util/Utils.java

index 6c52d59e75860d2656f513f1c6c3aa0c88945338..386e6fbe56a05fe8eae94b62acdaa290e3ad44e8 100644 (file)
@@ -69,6 +69,7 @@ import org.whispersystems.signalservice.api.messages.SignalServicePreview;
 import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceTypingMessage;
 import org.whispersystems.signalservice.api.push.ACI;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.api.util.DeviceNameUtil;
 import org.whispersystems.signalservice.api.util.InvalidNumberException;
 import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
@@ -165,11 +166,12 @@ class ManagerImpl implements Manager {
                 this.notifyAll();
             }
         });
-        disposable.add(account.getIdentityKeyStore().getIdentityChanges().subscribe(recipientId -> {
-            logger.trace("Archiving old sessions for {}", recipientId);
-            account.getAciSessionStore().archiveSessions(recipientId);
-            account.getPniSessionStore().archiveSessions(recipientId);
-            account.getSenderKeyStore().deleteSharedWith(recipientId);
+        disposable.add(account.getIdentityKeyStore().getIdentityChanges().subscribe(serviceId -> {
+            logger.trace("Archiving old sessions for {}", serviceId);
+            account.getAciSessionStore().archiveSessions(serviceId);
+            account.getPniSessionStore().archiveSessions(serviceId);
+            account.getSenderKeyStore().deleteSharedWith(serviceId);
+            final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
             final var profile = account.getProfileStore().getProfile(recipientId);
             if (profile != null) {
                 account.getProfileStore()
@@ -631,7 +633,11 @@ class ManagerImpl implements Manager {
             if (recipient instanceof RecipientIdentifier.Single r) {
                 try {
                     final var recipientId = context.getRecipientHelper().resolveRecipient(r);
-                    account.getMessageSendLogStore().deleteEntryForRecipientNonGroup(targetSentTimestamp, recipientId);
+                    account.getMessageSendLogStore()
+                            .deleteEntryForRecipientNonGroup(targetSentTimestamp,
+                                    account.getRecipientAddressResolver()
+                                            .resolveRecipientAddress(recipientId)
+                                            .getServiceId());
                 } catch (UnregisteredRecipientException ignored) {
                 }
             } else if (recipient instanceof RecipientIdentifier.Group r) {
@@ -689,7 +695,11 @@ class ManagerImpl implements Manager {
                 } catch (UnregisteredRecipientException e) {
                     continue;
                 }
-                account.getAciSessionStore().deleteAllSessions(recipientId);
+                final var serviceId = context.getAccount()
+                        .getRecipientAddressResolver()
+                        .resolveRecipientAddress(recipientId)
+                        .getServiceId();
+                account.getAciSessionStore().deleteAllSessions(serviceId);
             }
         }
     }
@@ -1035,13 +1045,13 @@ class ManagerImpl implements Manager {
         }
 
         final var address = account.getRecipientAddressResolver()
-                .resolveRecipientAddress(identityInfo.getRecipientId());
+                .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(identityInfo.getServiceId()));
         final var scannableFingerprint = context.getIdentityHelper()
-                .computeSafetyNumberForScanning(identityInfo.getRecipientId(), identityInfo.getIdentityKey());
+                .computeSafetyNumberForScanning(identityInfo.getServiceId(), identityInfo.getIdentityKey());
         return new Identity(address,
                 identityInfo.getIdentityKey(),
                 context.getIdentityHelper()
-                        .computeSafetyNumber(identityInfo.getRecipientId(), identityInfo.getIdentityKey()),
+                        .computeSafetyNumber(identityInfo.getServiceId(), identityInfo.getIdentityKey()),
                 scannableFingerprint == null ? null : scannableFingerprint.getSerialized(),
                 identityInfo.getTrustLevel(),
                 identityInfo.getDateAddedTimestamp());
@@ -1049,13 +1059,15 @@ class ManagerImpl implements Manager {
 
     @Override
     public List<Identity> getIdentities(RecipientIdentifier.Single recipient) {
-        IdentityInfo identity;
+        ServiceId serviceId;
         try {
-            identity = account.getIdentityKeyStore()
-                    .getIdentityInfo(context.getRecipientHelper().resolveRecipient(recipient));
+            serviceId = account.getRecipientAddressResolver()
+                    .resolveRecipientAddress(context.getRecipientHelper().resolveRecipient(recipient))
+                    .getServiceId();
         } catch (UnregisteredRecipientException e) {
-            identity = null;
+            return List.of();
         }
+        final var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
         return identity == null ? List.of() : List.of(toIdentity(identity));
     }
 
index b2de2cdd55261ccf64cae8ee374216c439822b26..2718bc26349648dce363590464b92d06fafe0180 100644 (file)
@@ -2,18 +2,21 @@ package org.asamk.signal.manager.actions;
 
 import org.asamk.signal.manager.helper.Context;
 import org.asamk.signal.manager.storage.recipients.RecipientId;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 public class RenewSessionAction implements HandleAction {
 
     private final RecipientId recipientId;
+    private final ServiceId serviceId;
 
-    public RenewSessionAction(final RecipientId recipientId) {
+    public RenewSessionAction(final RecipientId recipientId, final ServiceId serviceId) {
         this.recipientId = recipientId;
+        this.serviceId = serviceId;
     }
 
     @Override
     public void execute(Context context) throws Throwable {
-        context.getAccount().getAciSessionStore().archiveSessions(recipientId);
+        context.getAccount().getAciSessionStore().archiveSessions(serviceId);
         if (!recipientId.equals(context.getAccount().getSelfRecipientId())) {
             context.getSendHelper().sendNullMessage(recipientId);
         }
index 491dbb8facf781c4b990cc59bf8639f287036116..7c0562cc753c62796cdf485586e8e00da25f1f6e 100644 (file)
@@ -7,6 +7,7 @@ import org.signal.libsignal.metadata.ProtocolException;
 import org.signal.libsignal.protocol.message.CiphertextMessage;
 import org.signal.libsignal.protocol.message.DecryptionErrorMessage;
 import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
 
 import java.util.Optional;
@@ -14,22 +15,25 @@ import java.util.Optional;
 public class SendRetryMessageRequestAction implements HandleAction {
 
     private final RecipientId recipientId;
+    private final ServiceId serviceId;
     private final ProtocolException protocolException;
     private final SignalServiceEnvelope envelope;
 
     public SendRetryMessageRequestAction(
             final RecipientId recipientId,
+            final ServiceId serviceId,
             final ProtocolException protocolException,
             final SignalServiceEnvelope envelope
     ) {
         this.recipientId = recipientId;
+        this.serviceId = serviceId;
         this.protocolException = protocolException;
         this.envelope = envelope;
     }
 
     @Override
     public void execute(Context context) throws Throwable {
-        context.getAccount().getAciSessionStore().archiveSessions(recipientId);
+        context.getAccount().getAciSessionStore().archiveSessions(serviceId);
 
         int senderDevice = protocolException.getSenderDevice();
         Optional<GroupId> groupId = protocolException.getGroupId().isPresent() ? Optional.of(GroupId.unknownVersion(
index ce3973538ce4453673665fdd273558dc64892ffd..0adeaf60323ff3fe309dbe65ee071c37b5022caa 100644 (file)
@@ -2,6 +2,7 @@ package org.asamk.signal.manager.helper;
 
 import org.asamk.signal.manager.api.TrustLevel;
 import org.asamk.signal.manager.storage.SignalAccount;
+import org.asamk.signal.manager.storage.recipients.RecipientAddress;
 import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.asamk.signal.manager.util.Utils;
 import org.signal.libsignal.protocol.IdentityKey;
@@ -12,7 +13,7 @@ import org.signal.libsignal.protocol.fingerprint.ScannableFingerprint;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.whispersystems.signalservice.api.messages.SendMessageResult;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.io.IOException;
 import java.util.Arrays;
@@ -33,20 +34,23 @@ public class IdentityHelper {
     }
 
     public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) {
-        return trustIdentity(recipientId,
+        final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
+        return trustIdentity(serviceId,
                 identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
                 TrustLevel.TRUSTED_VERIFIED);
     }
 
     public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
-        return trustIdentity(recipientId,
-                identityKey -> safetyNumber.equals(computeSafetyNumber(recipientId, identityKey)),
+        final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
+        return trustIdentity(serviceId,
+                identityKey -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)),
                 TrustLevel.TRUSTED_VERIFIED);
     }
 
     public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
-        return trustIdentity(recipientId, identityKey -> {
-            final var fingerprint = computeSafetyNumberForScanning(recipientId, identityKey);
+        final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
+        return trustIdentity(serviceId, identityKey -> {
+            final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey);
             try {
                 return fingerprint != null && fingerprint.compareTo(safetyNumber);
             } catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
@@ -56,35 +60,39 @@ public class IdentityHelper {
     }
 
     public boolean trustIdentityAllKeys(RecipientId recipientId) {
-        return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
+        final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
+        return trustIdentity(serviceId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
     }
 
-    public String computeSafetyNumber(RecipientId recipientId, IdentityKey theirIdentityKey) {
-        var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
-        final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
+    public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) {
+        final Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
         return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
     }
 
-    public ScannableFingerprint computeSafetyNumberForScanning(RecipientId recipientId, IdentityKey theirIdentityKey) {
-        var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
-        final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
+    public ScannableFingerprint computeSafetyNumberForScanning(ServiceId serviceId, IdentityKey theirIdentityKey) {
+        final Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
         return fingerprint == null ? null : fingerprint.getScannableFingerprint();
     }
 
     private Fingerprint computeSafetyNumberFingerprint(
-            final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
+            final ServiceId serviceId, final IdentityKey theirIdentityKey
     ) {
+        final var address = account.getRecipientAddressResolver()
+                .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId));
+
         return Utils.computeSafetyNumber(capabilities.isUuid(),
-                account.getSelfAddress(),
+                account.getSelfRecipientAddress(),
                 account.getAciIdentityKeyPair().getPublicKey(),
-                theirAddress,
+                address.getServiceId().equals(serviceId)
+                        ? address
+                        : new RecipientAddress(serviceId.uuid(), address.number().orElse(null)),
                 theirIdentityKey);
     }
 
     private boolean trustIdentity(
-            RecipientId recipientId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
+            ServiceId serviceId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
     ) {
-        var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId);
+        var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
         if (identity == null) {
             return false;
         }
@@ -93,9 +101,11 @@ public class IdentityHelper {
             return false;
         }
 
-        account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel);
+        account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel);
         try {
-            var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
+            final var address = account.getRecipientAddressResolver()
+                    .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId))
+                    .toSignalServiceAddress();
             context.getSyncHelper().sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
         } catch (IOException e) {
             logger.warn("Failed to send verification sync message: {}", e.getMessage());
@@ -105,11 +115,13 @@ public class IdentityHelper {
     }
 
     public void handleIdentityFailure(
-            final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure
+            final RecipientId recipientId,
+            final ServiceId serviceId,
+            final SendMessageResult.IdentityFailure identityFailure
     ) {
         final var identityKey = identityFailure.getIdentityKey();
         if (identityKey != null) {
-            account.getIdentityKeyStore().saveIdentity(recipientId, identityKey);
+            account.getIdentityKeyStore().saveIdentity(serviceId, identityKey);
         } else {
             // Retrieve profile to get the current identity key from the server
             context.getProfileHelper().refreshRecipientProfile(recipientId);
index 89ff3e702f9bac3d235828620a00043b2dfbac84..fdf8bcd25030e17ec224ce728d10f69bb73b2c48 100644 (file)
@@ -42,7 +42,6 @@ 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.libsignal.protocol.SignalProtocolAddress;
 import org.signal.libsignal.protocol.message.DecryptionErrorMessage;
 import org.signal.libsignal.zkgroup.InvalidInputException;
 import org.signal.libsignal.zkgroup.profiles.ProfileKey;
@@ -57,6 +56,7 @@ import org.whispersystems.signalservice.api.messages.SignalServiceReceiptMessage
 import org.whispersystems.signalservice.api.messages.SignalServiceStoryMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.SignalServiceSyncMessage;
 import org.whispersystems.signalservice.api.messages.multidevice.StickerPackOperationMessage;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 
 import java.util.ArrayList;
@@ -137,16 +137,17 @@ public final class IncomingMessageHandler {
                 } else {
                     final var senderProfile = context.getProfileHelper().getRecipientProfile(sender);
                     final var selfProfile = context.getProfileHelper().getSelfProfile();
+                    final var serviceId = ServiceId.parseOrThrow(e.getSender());
                     if ((!sender.equals(account.getSelfRecipientId()) || e.getSenderDevice() != account.getDeviceId())
                             && 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));
+                        actions.add(new SendRetryMessageRequestAction(sender, serviceId, e, envelope));
                     } else {
                         logger.debug("Received invalid message, queuing renew session action.");
-                        actions.add(new RenewSessionAction(sender));
+                        actions.add(new RenewSessionAction(sender, serviceId));
                     }
                 }
                 exception = e;
@@ -176,9 +177,9 @@ public final class IncomingMessageHandler {
             account.getRecipientTrustedResolver().resolveRecipientTrusted(content.getSender());
         }
         if (envelope.isReceipt()) {
-            final var senderPair = getSender(envelope, content);
-            final var sender = senderPair.first();
-            final var senderDeviceId = senderPair.second();
+            final var senderDeviceAddress = getSender(envelope, content);
+            final var sender = senderDeviceAddress.serviceId();
+            final var senderDeviceId = senderDeviceAddress.deviceId();
             account.getMessageSendLogStore().deleteEntryForRecipient(envelope.getTimestamp(), sender, senderDeviceId);
         }
 
@@ -211,24 +212,23 @@ public final class IncomingMessageHandler {
             SignalServiceEnvelope envelope, SignalServiceContent content, ReceiveConfig receiveConfig
     ) {
         var actions = new ArrayList<HandleAction>();
-        final var senderPair = getSender(envelope, content);
-        final var sender = senderPair.first();
-        final var senderDeviceId = senderPair.second();
+        final var senderDeviceAddress = getSender(envelope, content);
+        final var sender = senderDeviceAddress.recipientId();
+        final var senderServiceId = senderDeviceAddress.serviceId();
+        final var senderDeviceId = senderDeviceAddress.deviceId();
         final var destination = getDestination(envelope);
 
         if (content.getReceiptMessage().isPresent()) {
             final var message = content.getReceiptMessage().get();
             if (message.isDeliveryReceipt()) {
                 account.getMessageSendLogStore()
-                        .deleteEntriesForRecipient(message.getTimestamps(), sender, senderDeviceId);
+                        .deleteEntriesForRecipient(message.getTimestamps(), senderServiceId, senderDeviceId);
             }
         }
 
         if (content.getSenderKeyDistributionMessage().isPresent()) {
             final var message = content.getSenderKeyDistributionMessage().get();
-            final var protocolAddress = new SignalProtocolAddress(context.getRecipientHelper()
-                    .resolveSignalServiceAddress(sender)
-                    .getIdentifier(), senderDeviceId);
+            final var protocolAddress = senderServiceId.toProtocolAddress(senderDeviceId);
             logger.debug("Received a sender key distribution message for distributionId {} from {}",
                     message.getDistributionId(),
                     protocolAddress);
@@ -242,7 +242,7 @@ public final class IncomingMessageHandler {
                     senderDeviceId,
                     message.getTimestamp());
             if (message.getDeviceId() == account.getDeviceId()) {
-                handleDecryptionErrorMessage(actions, sender, senderDeviceId, message);
+                handleDecryptionErrorMessage(actions, sender, senderServiceId, senderDeviceId, message);
             } else {
                 logger.debug("Request is for another one of our devices");
             }
@@ -274,7 +274,7 @@ public final class IncomingMessageHandler {
 
             actions.addAll(handleSignalServiceDataMessage(message,
                     false,
-                    sender,
+                    senderDeviceAddress,
                     destination,
                     receiveConfig.ignoreAttachments()));
         }
@@ -286,7 +286,7 @@ public final class IncomingMessageHandler {
 
         if (content.getSyncMessage().isPresent()) {
             var syncMessage = content.getSyncMessage().get();
-            actions.addAll(handleSyncMessage(syncMessage, sender, receiveConfig.ignoreAttachments()));
+            actions.addAll(handleSyncMessage(syncMessage, senderDeviceAddress, receiveConfig.ignoreAttachments()));
         }
 
         return actions;
@@ -295,11 +295,15 @@ public final class IncomingMessageHandler {
     private void handleDecryptionErrorMessage(
             final List<HandleAction> actions,
             final RecipientId sender,
+            final ServiceId senderServiceId,
             final int senderDeviceId,
             final DecryptionErrorMessage message
     ) {
         final var logEntries = account.getMessageSendLogStore()
-                .findMessages(sender, senderDeviceId, message.getTimestamp(), message.getRatchetKey().isEmpty());
+                .findMessages(senderServiceId,
+                        senderDeviceId,
+                        message.getTimestamp(),
+                        message.getRatchetKey().isEmpty());
 
         for (final var logEntry : logEntries) {
             actions.add(new ResendMessageAction(sender, message.getTimestamp(), logEntry));
@@ -307,13 +311,13 @@ public final class IncomingMessageHandler {
 
         if (message.getRatchetKey().isPresent()) {
             if (account.getAciSessionStore()
-                    .isCurrentRatchetKey(sender, senderDeviceId, message.getRatchetKey().get())) {
+                    .isCurrentRatchetKey(senderServiceId, senderDeviceId, message.getRatchetKey().get())) {
                 if (logEntries.isEmpty()) {
                     logger.debug("Renewing the session with sender");
-                    actions.add(new RenewSessionAction(sender));
+                    actions.add(new RenewSessionAction(sender, senderServiceId));
                 } else {
                     logger.trace("Archiving the session with sender, a resend message has already been queued");
-                    context.getAccount().getAciSessionStore().archiveSessions(sender);
+                    context.getAccount().getAciSessionStore().archiveSessions(senderServiceId);
                 }
             }
             return;
@@ -333,16 +337,16 @@ public final class IncomingMessageHandler {
                     sender,
                     senderDeviceId,
                     group.getDistributionId());
-            account.getSenderKeyStore().deleteSharedWith(sender, senderDeviceId, group.getDistributionId());
+            account.getSenderKeyStore().deleteSharedWith(senderServiceId, senderDeviceId, group.getDistributionId());
         }
         if (!found) {
             logger.debug("Reset all shared sender keys with this recipient, no related message found in send log");
-            account.getSenderKeyStore().deleteSharedWith(sender);
+            account.getSenderKeyStore().deleteSharedWith(senderServiceId);
         }
     }
 
     private List<HandleAction> handleSyncMessage(
-            final SignalServiceSyncMessage syncMessage, final RecipientId sender, final boolean ignoreAttachments
+            final SignalServiceSyncMessage syncMessage, final DeviceAddress sender, final boolean ignoreAttachments
     ) {
         var actions = new ArrayList<HandleAction>();
         account.setMultiDevice(true);
@@ -353,12 +357,16 @@ public final class IncomingMessageHandler {
                 actions.addAll(handleSignalServiceDataMessage(message.getDataMessage().get(),
                         true,
                         sender,
-                        destination == null ? null : context.getRecipientHelper().resolveRecipient(destination),
+                        destination == null
+                                ? null
+                                : new DeviceAddress(context.getRecipientHelper().resolveRecipient(destination),
+                                        destination.getServiceId(),
+                                        0),
                         ignoreAttachments));
             }
             if (message.getStoryMessage().isPresent()) {
                 actions.addAll(handleSignalServiceStoryMessage(message.getStoryMessage().get(),
-                        sender,
+                        sender.recipientId(),
                         ignoreAttachments));
             }
         }
@@ -423,8 +431,7 @@ public final class IncomingMessageHandler {
         if (syncMessage.getVerified().isPresent()) {
             final var verifiedMessage = syncMessage.getVerified().get();
             account.getIdentityKeyStore()
-                    .setIdentityTrustLevel(account.getRecipientTrustedResolver()
-                                    .resolveRecipientTrusted(verifiedMessage.getDestination()),
+                    .setIdentityTrustLevel(verifiedMessage.getDestination().getServiceId(),
                             verifiedMessage.getIdentityKey(),
                             TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
         }
@@ -575,8 +582,8 @@ public final class IncomingMessageHandler {
     private List<HandleAction> handleSignalServiceDataMessage(
             SignalServiceDataMessage message,
             boolean isSync,
-            RecipientId source,
-            RecipientId destination,
+            DeviceAddress source,
+            DeviceAddress destination,
             boolean ignoreAttachments
     ) {
         var actions = new ArrayList<HandleAction>();
@@ -616,19 +623,19 @@ public final class IncomingMessageHandler {
                         }
                         case DELIVER:
                             if (groupV1 == null && !isSync) {
-                                actions.add(new SendGroupInfoRequestAction(source, groupId));
+                                actions.add(new SendGroupInfoRequestAction(source.recipientId(), groupId));
                             }
                             break;
                         case QUIT: {
                             if (groupV1 != null) {
-                                groupV1.removeMember(source);
+                                groupV1.removeMember(source.recipientId());
                                 account.getGroupStore().updateGroup(groupV1);
                             }
                             break;
                         }
                         case REQUEST_INFO:
                             if (groupV1 != null && !isSync) {
-                                actions.add(new SendGroupInfoAction(source, groupV1.getGroupId()));
+                                actions.add(new SendGroupInfoAction(source.recipientId(), groupV1.getGroupId()));
                             }
                             break;
                     }
@@ -643,7 +650,7 @@ public final class IncomingMessageHandler {
 
         final var conversationPartnerAddress = isSync ? destination : source;
         if (conversationPartnerAddress != null && message.isEndSession()) {
-            account.getAciSessionStore().deleteAllSessions(conversationPartnerAddress);
+            account.getAciSessionStore().deleteAllSessions(conversationPartnerAddress.serviceId());
         }
         if (message.isExpirationUpdate() || message.getBody().isPresent()) {
             if (message.getGroupContext().isPresent()) {
@@ -662,7 +669,7 @@ public final class IncomingMessageHandler {
                 }
             } else if (conversationPartnerAddress != null) {
                 context.getContactHelper()
-                        .setExpirationTimer(conversationPartnerAddress, message.getExpiresInSeconds());
+                        .setExpirationTimer(conversationPartnerAddress.recipientId(), message.getExpiresInSeconds());
             }
         }
         if (!ignoreAttachments) {
@@ -698,7 +705,7 @@ public final class IncomingMessageHandler {
             }
         }
         if (message.getProfileKey().isPresent()) {
-            handleIncomingProfileKey(message.getProfileKey().get(), source);
+            handleIncomingProfileKey(message.getProfileKey().get(), source.recipientId());
         }
         if (message.getSticker().isPresent()) {
             final var messageSticker = message.getSticker().get();
@@ -769,24 +776,29 @@ public final class IncomingMessageHandler {
         this.account.getProfileStore().storeProfileKey(source, profileKey);
     }
 
-    private Pair<RecipientId, Integer> getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
+    private DeviceAddress getSender(SignalServiceEnvelope envelope, SignalServiceContent content) {
         if (!envelope.isUnidentifiedSender() && envelope.hasSourceUuid()) {
-            return new Pair<>(context.getRecipientHelper().resolveRecipient(envelope.getSourceAddress()),
+            return new DeviceAddress(context.getRecipientHelper().resolveRecipient(envelope.getSourceAddress()),
+                    envelope.getSourceAddress().getServiceId(),
                     envelope.getSourceDevice());
         } else {
-            return new Pair<>(context.getRecipientHelper().resolveRecipient(content.getSender()),
+            return new DeviceAddress(context.getRecipientHelper().resolveRecipient(content.getSender()),
+                    content.getSender().getServiceId(),
                     content.getSenderDevice());
         }
     }
 
-    private RecipientId getDestination(SignalServiceEnvelope envelope) {
+    private DeviceAddress getDestination(SignalServiceEnvelope envelope) {
         if (!envelope.hasDestinationUuid()) {
-            return account.getSelfRecipientId();
+            return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
         }
         final var addressOptional = SignalServiceAddress.fromRaw(envelope.getDestinationUuid(), null);
         if (addressOptional.isEmpty()) {
-            return account.getSelfRecipientId();
+            return new DeviceAddress(account.getSelfRecipientId(), account.getAci(), account.getDeviceId());
         }
-        return context.getRecipientHelper().resolveRecipient(addressOptional.get());
+        final var address = addressOptional.get();
+        return new DeviceAddress(context.getRecipientHelper().resolveRecipient(address), address.getServiceId(), 0);
     }
+
+    private record DeviceAddress(RecipientId recipientId, ServiceId serviceId, int deviceId) {}
 }
index 45ff107a1e76792a788dcedc76cf1221c4a57201..3379c90522bcfcde4743e596bbc5a25e5e207078 100644 (file)
@@ -346,7 +346,7 @@ public final class ProfileHelper {
             try {
                 logger.trace("Storing identity");
                 final var identityKey = new IdentityKey(Base64.getDecoder().decode(encryptedProfile.getIdentityKey()));
-                account.getIdentityKeyStore().saveIdentity(recipientId, identityKey);
+                account.getIdentityKeyStore().saveIdentity(p.getProfile().getServiceId(), identityKey);
             } catch (InvalidKeyException ignored) {
                 logger.warn("Got invalid identity key in profile for {}",
                         context.getRecipientHelper().resolveSignalServiceAddress(recipientId).getIdentifier());
index 717db81219c4a50be8923d39343c3191d77c8fdf..95e84f083e720f94c3884b033618620231a751e6 100644 (file)
@@ -486,7 +486,10 @@ public class SendHelper {
                 continue;
             }
 
-            final var identity = account.getIdentityKeyStore().getIdentityInfo(recipientId);
+            final var serviceId = account.getRecipientAddressResolver()
+                    .resolveRecipientAddress(recipientId)
+                    .getServiceId();
+            final var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
             if (identity == null || !identity.getTrustLevel().isTrusted()) {
                 continue;
             }
@@ -531,7 +534,7 @@ public class SendHelper {
         final var recipientIdList = new ArrayList<>(recipientIds);
 
         long keyCreateTime = account.getSenderKeyStore()
-                .getCreateTimeForOurKey(account.getSelfRecipientId(), account.getDeviceId(), distributionId);
+                .getCreateTimeForOurKey(account.getAci(), account.getDeviceId(), distributionId);
         long keyAge = System.currentTimeMillis() - keyCreateTime;
 
         if (keyCreateTime != -1 && keyAge > TimeUnit.DAYS.toMillis(14)) {
@@ -540,7 +543,7 @@ public class SendHelper {
                     keyCreateTime,
                     keyAge,
                     TimeUnit.MILLISECONDS.toDays(keyAge));
-            account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
+            account.getSenderKeyStore().deleteOurKey(account.getAci(), distributionId);
         }
 
         List<SignalServiceAddress> addresses = recipientIdList.stream()
@@ -573,11 +576,11 @@ public class SendHelper {
             return null;
         } catch (NoSessionException e) {
             logger.warn("No session. Falling back to legacy sends.", e);
-            account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
+            account.getSenderKeyStore().deleteOurKey(account.getAci(), distributionId);
             return null;
         } catch (InvalidKeyException e) {
             logger.warn("Invalid key. Falling back to legacy sends.", e);
-            account.getSenderKeyStore().deleteOurKey(account.getSelfRecipientId(), distributionId);
+            account.getSenderKeyStore().deleteOurKey(account.getAci(), distributionId);
             return null;
         } catch (InvalidRegistrationIdException e) {
             logger.warn("Invalid registrationId. Falling back to legacy sends.", e);
@@ -685,7 +688,8 @@ public class SendHelper {
         }
         if (r.getIdentityFailure() != null) {
             final var recipientId = context.getRecipientHelper().resolveRecipient(r.getAddress());
-            context.getIdentityHelper().handleIdentityFailure(recipientId, r.getIdentityFailure());
+            context.getIdentityHelper()
+                    .handleIdentityFailure(recipientId, r.getAddress().getServiceId(), r.getIdentityFailure());
         }
     }
 
index bc785099df4db2c4651f0e02ce0ae39390150243..3037d7c303c98b948026cb2de892248fef43346a 100644 (file)
@@ -144,11 +144,12 @@ public class StorageHelper {
             try {
                 logger.trace("Storing identity key {}", recipientId);
                 final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get());
-                account.getIdentityKeyStore().saveIdentity(recipientId, identityKey);
+                account.getIdentityKeyStore().saveIdentity(address.getServiceId(), identityKey);
 
                 final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState());
                 if (trustLevel != null) {
-                    account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identityKey, trustLevel);
+                    account.getIdentityKeyStore()
+                            .setIdentityTrustLevel(address.getServiceId(), identityKey, trustLevel);
                 }
             } catch (InvalidKeyException e) {
                 logger.warn("Received invalid contact identity key from storage");
index 73662d22e6da6baa37c2e48f52013e750d039827..608876199294cc0c610120898b28fbacca1d02ce 100644 (file)
@@ -132,7 +132,7 @@ public class SyncHelper {
                     final var contact = contactPair.second();
                     final var address = context.getRecipientHelper().resolveSignalServiceAddress(recipientId);
 
-                    var currentIdentity = account.getIdentityKeyStore().getIdentityInfo(recipientId);
+                    var currentIdentity = account.getIdentityKeyStore().getIdentityInfo(address.getServiceId());
                     VerifiedMessage verifiedMessage = null;
                     if (currentIdentity != null) {
                         verifiedMessage = new VerifiedMessage(address,
@@ -319,8 +319,7 @@ public class SyncHelper {
             if (c.getVerified().isPresent()) {
                 final var verifiedMessage = c.getVerified().get();
                 account.getIdentityKeyStore()
-                        .setIdentityTrustLevel(account.getRecipientTrustedResolver()
-                                        .resolveRecipientTrusted(verifiedMessage.getDestination()),
+                        .setIdentityTrustLevel(verifiedMessage.getDestination().getServiceId(),
                                 verifiedMessage.getIdentityKey(),
                                 TrustLevel.fromVerifiedState(verifiedMessage.getVerified()));
             }
index c077708b21ba73cec73d354f6ea1a071b7735555..5093b9be827debe3773dc86d973327a8a085505d 100644 (file)
@@ -22,7 +22,7 @@ import java.sql.SQLException;
 public class AccountDatabase extends Database {
 
     private final static Logger logger = LoggerFactory.getLogger(AccountDatabase.class);
-    private static final long DATABASE_VERSION = 9;
+    private static final long DATABASE_VERSION = 10;
 
     private AccountDatabase(final HikariDataSource dataSource) {
         super(logger, DATABASE_VERSION, dataSource);
@@ -212,5 +212,81 @@ public class AccountDatabase extends Database {
                                         """);
             }
         }
+        if (oldVersion < 10) {
+            logger.debug("Updating database: Key tables on serviceId instead of recipientId");
+            try (final var statement = connection.createStatement()) {
+                statement.executeUpdate("""
+                                        CREATE TABLE identity2 (
+                                          _id INTEGER PRIMARY KEY,
+                                          uuid BLOB UNIQUE NOT NULL,
+                                          identity_key BLOB NOT NULL,
+                                          added_timestamp INTEGER NOT NULL,
+                                          trust_level INTEGER NOT NULL
+                                        ) STRICT;
+                                        INSERT INTO identity2 (_id, uuid, identity_key, added_timestamp, trust_level)
+                                          SELECT i._id, r.uuid, i.identity_key, i.added_timestamp, i.trust_level
+                                          FROM identity i LEFT JOIN recipient r ON i.recipient_id = r._id
+                                          WHERE uuid IS NOT NULL;
+                                        DROP TABLE identity;
+                                        ALTER TABLE identity2 RENAME TO identity;
+
+                                        DROP INDEX msl_recipient_index;
+                                        ALTER TABLE message_send_log ADD COLUMN uuid BLOB;
+                                        UPDATE message_send_log
+                                          SET uuid = r.uuid
+                                          FROM message_send_log i, (SELECT _id, uuid FROM recipient) AS r
+                                          WHERE i.recipient_id = r._id;
+                                        DELETE FROM message_send_log WHERE uuid IS NULL;
+                                        ALTER TABLE message_send_log DROP COLUMN recipient_id;
+                                        CREATE INDEX msl_recipient_index ON message_send_log (uuid, device_id, content_id);
+
+                                        CREATE TABLE sender_key2 (
+                                          _id INTEGER PRIMARY KEY,
+                                          uuid BLOB NOT NULL,
+                                          device_id INTEGER NOT NULL,
+                                          distribution_id BLOB NOT NULL,
+                                          record BLOB NOT NULL,
+                                          created_timestamp INTEGER NOT NULL,
+                                          UNIQUE(uuid, device_id, distribution_id)
+                                        ) STRICT;
+                                        INSERT INTO sender_key2 (_id, uuid, device_id, distribution_id, record, created_timestamp)
+                                          SELECT s._id, r.uuid, s.device_id, s.distribution_id, s.record, s.created_timestamp
+                                          FROM sender_key s LEFT JOIN recipient r ON s.recipient_id = r._id
+                                          WHERE uuid IS NOT NULL;
+                                        DROP TABLE sender_key;
+                                        ALTER TABLE sender_key2 RENAME TO sender_key;
+
+                                        CREATE TABLE sender_key_shared2 (
+                                          _id INTEGER PRIMARY KEY,
+                                          uuid BLOB NOT NULL,
+                                          device_id INTEGER NOT NULL,
+                                          distribution_id BLOB NOT NULL,
+                                          timestamp INTEGER NOT NULL,
+                                          UNIQUE(uuid, device_id, distribution_id)
+                                        ) STRICT;
+                                        INSERT INTO sender_key_shared2 (_id, uuid, device_id, distribution_id, timestamp)
+                                          SELECT s._id, r.uuid, s.device_id, s.distribution_id, s.timestamp
+                                          FROM sender_key_shared s LEFT JOIN recipient r ON s.recipient_id = r._id
+                                          WHERE uuid IS NOT NULL;
+                                        DROP TABLE sender_key_shared;
+                                        ALTER TABLE sender_key_shared2 RENAME TO sender_key_shared;
+
+                                        CREATE TABLE session2 (
+                                          _id INTEGER PRIMARY KEY,
+                                          account_id_type INTEGER NOT NULL,
+                                          uuid BLOB NOT NULL,
+                                          device_id INTEGER NOT NULL,
+                                          record BLOB NOT NULL,
+                                          UNIQUE(account_id_type, uuid, device_id)
+                                        ) STRICT;
+                                        INSERT INTO session2 (_id, account_id_type, uuid, device_id, record)
+                                          SELECT s._id, s.account_id_type, r.uuid, s.device_id, s.record
+                                          FROM session s LEFT JOIN recipient r ON s.recipient_id = r._id
+                                          WHERE uuid IS NOT NULL;
+                                        DROP TABLE session;
+                                        ALTER TABLE session2 RENAME TO session;
+                                        """);
+            }
+        }
     }
 }
index 78601e07c3877a468c6d4ea35bc89063a578cdcb..72f6f3d5b5a5eae8e2d9ec930fbdb3793fa7a691 100644 (file)
@@ -383,6 +383,14 @@ public class SignalAccount implements Closeable {
         this.storageManifestVersion = -1;
         this.setStorageManifest(null);
         this.storageKey = null;
+        final var aciPublicKey = getAciIdentityKeyPair().getPublicKey();
+        getIdentityKeyStore().saveIdentity(getAci(), aciPublicKey);
+        getIdentityKeyStore().setIdentityTrustLevel(getAci(), aciPublicKey, TrustLevel.TRUSTED_VERIFIED);
+        if (getPniIdentityKeyPair() != null) {
+            final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
+            getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
+            getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
+        }
     }
 
     private void migrateLegacyConfigs() {
@@ -400,21 +408,21 @@ public class SignalAccount implements Closeable {
     }
 
     private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
-        getAciSessionStore().mergeRecipients(recipientId, toBeMergedRecipientId);
-        getPniSessionStore().mergeRecipients(recipientId, toBeMergedRecipientId);
-        getIdentityKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId);
         getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId);
         getGroupStore().mergeRecipients(recipientId, toBeMergedRecipientId);
-        getSenderKeyStore().mergeRecipients(recipientId, toBeMergedRecipientId);
     }
 
     public void removeRecipient(final RecipientId recipientId) {
-        getAciSessionStore().deleteAllSessions(recipientId);
-        getPniSessionStore().deleteAllSessions(recipientId);
-        getIdentityKeyStore().deleteIdentity(recipientId);
-        getMessageCache().deleteMessages(recipientId);
-        getSenderKeyStore().deleteAll(recipientId);
         getRecipientStore().deleteRecipientData(recipientId);
+        getMessageCache().deleteMessages(recipientId);
+        final var recipientAddress = getRecipientStore().resolveRecipientAddress(recipientId);
+        if (recipientAddress.uuid().isPresent()) {
+            final var serviceId = ServiceId.from(recipientAddress.uuid().get());
+            getAciSessionStore().deleteAllSessions(serviceId);
+            getPniSessionStore().deleteAllSessions(serviceId);
+            getIdentityKeyStore().deleteIdentity(serviceId);
+            getSenderKeyStore().deleteAll(serviceId);
+        }
     }
 
     public static File getFileName(File dataPath, String account) {
@@ -646,12 +654,18 @@ public class SignalAccount implements Closeable {
         }
         final var legacySessionsPath = getSessionsPath(dataPath, accountPath);
         if (legacySessionsPath.exists()) {
-            LegacySessionStore.migrate(legacySessionsPath, getRecipientResolver(), getAciSessionStore());
+            LegacySessionStore.migrate(legacySessionsPath,
+                    getRecipientResolver(),
+                    getRecipientAddressResolver(),
+                    getAciSessionStore());
             migratedLegacyConfig = true;
         }
         final var legacyIdentitiesPath = getIdentitiesPath(dataPath, accountPath);
         if (legacyIdentitiesPath.exists()) {
-            LegacyIdentityKeyStore.migrate(legacyIdentitiesPath, getRecipientResolver(), getIdentityKeyStore());
+            LegacyIdentityKeyStore.migrate(legacyIdentitiesPath,
+                    getRecipientResolver(),
+                    getRecipientAddressResolver(),
+                    getIdentityKeyStore());
             migratedLegacyConfig = true;
         }
         final var legacySignalProtocolStore = rootNode.hasNonNull("axolotlStore")
@@ -672,12 +686,18 @@ public class SignalAccount implements Closeable {
 
         final var legacySenderKeysPath = getSenderKeysPath(dataPath, accountPath);
         if (legacySenderKeysPath.exists()) {
-            LegacySenderKeyRecordStore.migrate(legacySenderKeysPath, getRecipientResolver(), getSenderKeyStore());
+            LegacySenderKeyRecordStore.migrate(legacySenderKeysPath,
+                    getRecipientResolver(),
+                    getRecipientAddressResolver(),
+                    getSenderKeyStore());
             migratedLegacyConfig = true;
         }
         final var legacySenderKeysSharedPath = getSharedSenderKeysFile(dataPath, accountPath);
         if (legacySenderKeysSharedPath.exists()) {
-            LegacySenderKeySharedStore.migrate(legacySenderKeysSharedPath, getRecipientResolver(), getSenderKeyStore());
+            LegacySenderKeySharedStore.migrate(legacySenderKeysSharedPath,
+                    getRecipientResolver(),
+                    getRecipientAddressResolver(),
+                    getSenderKeyStore());
             migratedLegacyConfig = true;
         }
         if (rootNode.hasNonNull("groupStore")) {
@@ -770,9 +790,12 @@ public class SignalAccount implements Closeable {
         if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
             logger.debug("Migrating legacy identity session store.");
             for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) {
-                RecipientId recipientId = getRecipientStore().resolveRecipientTrusted(identity.getAddress());
-                getIdentityKeyStore().saveIdentity(recipientId, identity.getIdentityKey());
-                getIdentityKeyStore().setIdentityTrustLevel(recipientId,
+                if (identity.getAddress().uuid().isEmpty()) {
+                    continue;
+                }
+                final var serviceId = identity.getAddress().getServiceId();
+                getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey());
+                getIdentityKeyStore().setIdentityTrustLevel(serviceId,
                         identity.getIdentityKey(),
                         identity.getTrustLevel());
             }
@@ -1107,25 +1130,17 @@ public class SignalAccount implements Closeable {
 
     public SessionStore getAciSessionStore() {
         return getOrCreate(() -> aciSessionStore,
-                () -> aciSessionStore = new SessionStore(getAccountDatabase(),
-                        ServiceIdType.ACI,
-                        getRecipientResolver(),
-                        getRecipientIdCreator()));
+                () -> aciSessionStore = new SessionStore(getAccountDatabase(), ServiceIdType.ACI));
     }
 
     public SessionStore getPniSessionStore() {
         return getOrCreate(() -> pniSessionStore,
-                () -> pniSessionStore = new SessionStore(getAccountDatabase(),
-                        ServiceIdType.PNI,
-                        getRecipientResolver(),
-                        getRecipientIdCreator()));
+                () -> pniSessionStore = new SessionStore(getAccountDatabase(), ServiceIdType.PNI));
     }
 
     public IdentityKeyStore getIdentityKeyStore() {
         return getOrCreate(() -> identityKeyStore,
-                () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(),
-                        getRecipientIdCreator(),
-                        trustNewIdentity));
+                () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(), trustNewIdentity));
     }
 
     public SignalIdentityKeyStore getAciIdentityKeyStore() {
@@ -1207,11 +1222,7 @@ public class SignalAccount implements Closeable {
     }
 
     public SenderKeyStore getSenderKeyStore() {
-        return getOrCreate(() -> senderKeyStore,
-                () -> senderKeyStore = new SenderKeyStore(getAccountDatabase(),
-                        getRecipientAddressResolver(),
-                        getRecipientResolver(),
-                        getRecipientIdCreator()));
+        return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getAccountDatabase()));
     }
 
     public ConfigurationStore getConfigurationStore() {
@@ -1235,7 +1246,7 @@ public class SignalAccount implements Closeable {
 
     public MessageSendLogStore getMessageSendLogStore() {
         return getOrCreate(() -> messageSendLogStore,
-                () -> messageSendLogStore = new MessageSendLogStore(getRecipientResolver(), getAccountDatabase()));
+                () -> messageSendLogStore = new MessageSendLogStore(getAccountDatabase()));
     }
 
     public CredentialsProvider getCredentialsProvider() {
@@ -1350,6 +1361,9 @@ public class SignalAccount implements Closeable {
 
     public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
         pniIdentityKeyPair = identityKeyPair;
+        final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
+        getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
+        getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
         save();
     }
 
@@ -1553,10 +1567,13 @@ public class SignalAccount implements Closeable {
         getAciSessionStore().archiveAllSessions();
         getPniSessionStore().archiveAllSessions();
         getSenderKeyStore().deleteAll();
-        final var recipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
-        final var publicKey = getAciIdentityKeyPair().getPublicKey();
-        getIdentityKeyStore().saveIdentity(recipientId, publicKey);
-        getIdentityKeyStore().setIdentityTrustLevel(recipientId, publicKey, TrustLevel.TRUSTED_VERIFIED);
+        getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
+        final var aciPublicKey = getAciIdentityKeyPair().getPublicKey();
+        getIdentityKeyStore().saveIdentity(getAci(), aciPublicKey);
+        getIdentityKeyStore().setIdentityTrustLevel(getAci(), aciPublicKey, TrustLevel.TRUSTED_VERIFIED);
+        final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
+        getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
+        getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
     }
 
     public void deleteAccountData() throws IOException {
index 571f564d86bc93b85beeecc01d0d8e47ada9a3be..84f7b06ff200e7243310c7b8110df92980e2fcb6 100644 (file)
@@ -1,27 +1,27 @@
 package org.asamk.signal.manager.storage.identities;
 
 import org.asamk.signal.manager.api.TrustLevel;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.signal.libsignal.protocol.IdentityKey;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 public class IdentityInfo {
 
-    private final RecipientId recipientId;
+    private final ServiceId serviceId;
     private final IdentityKey identityKey;
     private final TrustLevel trustLevel;
     private final long addedTimestamp;
 
     IdentityInfo(
-            final RecipientId recipientId, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp
+            final ServiceId serviceId, IdentityKey identityKey, TrustLevel trustLevel, long addedTimestamp
     ) {
-        this.recipientId = recipientId;
+        this.serviceId = serviceId;
         this.identityKey = identityKey;
         this.trustLevel = trustLevel;
         this.addedTimestamp = addedTimestamp;
     }
 
-    public RecipientId getRecipientId() {
-        return recipientId;
+    public ServiceId getServiceId() {
+        return serviceId;
     }
 
     public IdentityKey getIdentityKey() {
index 3971532f67b8df4243b962a7c30a0091d39b1e4f..9ae8dda873faeb0b25d9d96f9810ad3a20c7d4c3 100644 (file)
@@ -3,13 +3,12 @@ package org.asamk.signal.manager.storage.identities;
 import org.asamk.signal.manager.api.TrustLevel;
 import org.asamk.signal.manager.storage.Database;
 import org.asamk.signal.manager.storage.Utils;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
 import org.signal.libsignal.protocol.IdentityKey;
 import org.signal.libsignal.protocol.InvalidKeyException;
 import org.signal.libsignal.protocol.state.IdentityKeyStore.Direction;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
@@ -25,9 +24,8 @@ public class IdentityKeyStore {
     private final static Logger logger = LoggerFactory.getLogger(IdentityKeyStore.class);
     private static final String TABLE_IDENTITY = "identity";
     private final Database database;
-    private final RecipientIdCreator recipientIdCreator;
     private final TrustNewIdentity trustNewIdentity;
-    private final PublishSubject<RecipientId> identityChanges = PublishSubject.create();
+    private final PublishSubject<ServiceId> identityChanges = PublishSubject.create();
 
     private boolean isRetryingDecryption = false;
 
@@ -37,42 +35,37 @@ public class IdentityKeyStore {
             statement.executeUpdate("""
                                     CREATE TABLE identity (
                                       _id INTEGER PRIMARY KEY,
-                                      recipient_id INTEGER UNIQUE NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
+                                      uuid BLOB UNIQUE NOT NULL,
                                       identity_key BLOB NOT NULL,
                                       added_timestamp INTEGER NOT NULL,
                                       trust_level INTEGER NOT NULL
-                                    );
+                                    ) STRICT;
                                     """);
         }
     }
 
-    public IdentityKeyStore(
-            final Database database,
-            final RecipientIdCreator recipientIdCreator,
-            final TrustNewIdentity trustNewIdentity
-    ) {
+    public IdentityKeyStore(final Database database, final TrustNewIdentity trustNewIdentity) {
         this.database = database;
-        this.recipientIdCreator = recipientIdCreator;
         this.trustNewIdentity = trustNewIdentity;
     }
 
-    public Observable<RecipientId> getIdentityChanges() {
+    public Observable<ServiceId> getIdentityChanges() {
         return identityChanges;
     }
 
-    public boolean saveIdentity(final RecipientId recipientId, final IdentityKey identityKey) {
+    public boolean saveIdentity(final ServiceId serviceId, final IdentityKey identityKey) {
         if (isRetryingDecryption) {
             return false;
         }
         try (final var connection = database.getConnection()) {
-            final var identityInfo = loadIdentity(connection, recipientId);
+            final var identityInfo = loadIdentity(connection, serviceId);
             if (identityInfo != null && identityInfo.getIdentityKey().equals(identityKey)) {
                 // Identity already exists, not updating the trust level
-                logger.trace("Not storing new identity for recipient {}, identity already stored", recipientId);
+                logger.trace("Not storing new identity for recipient {}, identity already stored", serviceId);
                 return false;
             }
 
-            saveNewIdentity(connection, recipientId, identityKey, identityInfo == null);
+            saveNewIdentity(connection, serviceId, identityKey, identityInfo == null);
             return true;
         } catch (SQLException e) {
             throw new RuntimeException("Failed update identity store", e);
@@ -83,24 +76,24 @@ public class IdentityKeyStore {
         isRetryingDecryption = retryingDecryption;
     }
 
-    public boolean setIdentityTrustLevel(RecipientId recipientId, IdentityKey identityKey, TrustLevel trustLevel) {
+    public boolean setIdentityTrustLevel(ServiceId serviceId, IdentityKey identityKey, TrustLevel trustLevel) {
         try (final var connection = database.getConnection()) {
-            final var identityInfo = loadIdentity(connection, recipientId);
+            final var identityInfo = loadIdentity(connection, serviceId);
             if (identityInfo == null) {
-                logger.debug("Not updating trust level for recipient {}, identity not found", recipientId);
+                logger.debug("Not updating trust level for recipient {}, identity not found", serviceId);
                 return false;
             }
             if (!identityInfo.getIdentityKey().equals(identityKey)) {
-                logger.debug("Not updating trust level for recipient {}, different identity found", recipientId);
+                logger.debug("Not updating trust level for recipient {}, different identity found", serviceId);
                 return false;
             }
             if (identityInfo.getTrustLevel() == trustLevel) {
-                logger.trace("Not updating trust level for recipient {}, trust level already matches", recipientId);
+                logger.trace("Not updating trust level for recipient {}, trust level already matches", serviceId);
                 return false;
             }
 
-            logger.debug("Updating trust level for recipient {} with trust {}", recipientId, trustLevel);
-            final var newIdentityInfo = new IdentityInfo(recipientId,
+            logger.debug("Updating trust level for recipient {} with trust {}", serviceId, trustLevel);
+            final var newIdentityInfo = new IdentityInfo(serviceId,
                     identityKey,
                     trustLevel,
                     identityInfo.getDateAddedTimestamp());
@@ -111,41 +104,41 @@ public class IdentityKeyStore {
         }
     }
 
-    public boolean isTrustedIdentity(RecipientId recipientId, IdentityKey identityKey, Direction direction) {
+    public boolean isTrustedIdentity(ServiceId serviceId, IdentityKey identityKey, Direction direction) {
         if (trustNewIdentity == TrustNewIdentity.ALWAYS) {
             return true;
         }
 
         try (final var connection = database.getConnection()) {
             // TODO implement possibility for different handling of incoming/outgoing trust decisions
-            var identityInfo = loadIdentity(connection, recipientId);
+            var identityInfo = loadIdentity(connection, serviceId);
             if (identityInfo == null) {
-                logger.debug("Initial identity found for {}, saving.", recipientId);
-                saveNewIdentity(connection, recipientId, identityKey, true);
-                identityInfo = loadIdentity(connection, recipientId);
+                logger.debug("Initial identity found for {}, saving.", serviceId);
+                saveNewIdentity(connection, serviceId, identityKey, true);
+                identityInfo = loadIdentity(connection, serviceId);
             } else if (!identityInfo.getIdentityKey().equals(identityKey)) {
                 // Identity found, but different
                 if (direction == Direction.SENDING) {
-                    logger.debug("Changed identity found for {}, saving.", recipientId);
-                    saveNewIdentity(connection, recipientId, identityKey, false);
-                    identityInfo = loadIdentity(connection, recipientId);
+                    logger.debug("Changed identity found for {}, saving.", serviceId);
+                    saveNewIdentity(connection, serviceId, identityKey, false);
+                    identityInfo = loadIdentity(connection, serviceId);
                 } else {
-                    logger.trace("Trusting identity for {} for {}: {}", recipientId, direction, false);
+                    logger.trace("Trusting identity for {} for {}: {}", serviceId, direction, false);
                     return false;
                 }
             }
 
             final var isTrusted = identityInfo != null && identityInfo.isTrusted();
-            logger.trace("Trusting identity for {} for {}: {}", recipientId, direction, isTrusted);
+            logger.trace("Trusting identity for {} for {}: {}", serviceId, direction, isTrusted);
             return isTrusted;
         } catch (SQLException e) {
             throw new RuntimeException("Failed read from identity store", e);
         }
     }
 
-    public IdentityInfo getIdentityInfo(RecipientId recipientId) {
+    public IdentityInfo getIdentityInfo(ServiceId serviceId) {
         try (final var connection = database.getConnection()) {
-            return loadIdentity(connection, recipientId);
+            return loadIdentity(connection, serviceId);
         } catch (SQLException e) {
             throw new RuntimeException("Failed read from identity store", e);
         }
@@ -155,7 +148,7 @@ public class IdentityKeyStore {
         try (final var connection = database.getConnection()) {
             final var sql = (
                     """
-                    SELECT i.recipient_id, i.identity_key, i.added_timestamp, i.trust_level
+                    SELECT i.uuid, i.identity_key, i.added_timestamp, i.trust_level
                     FROM %s AS i
                     """
             ).formatted(TABLE_IDENTITY);
@@ -167,32 +160,9 @@ public class IdentityKeyStore {
         }
     }
 
-    public void mergeRecipients(final RecipientId recipientId, final RecipientId toBeMergedRecipientId) {
+    public void deleteIdentity(final ServiceId serviceId) {
         try (final var connection = database.getConnection()) {
-            connection.setAutoCommit(false);
-            final var sql = (
-                    """
-                    UPDATE OR IGNORE %s
-                    SET recipient_id = ?
-                    WHERE recipient_id = ?
-                    """
-            ).formatted(TABLE_IDENTITY);
-            try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
-                statement.setLong(2, toBeMergedRecipientId.id());
-                statement.executeUpdate();
-            }
-
-            deleteIdentity(connection, toBeMergedRecipientId);
-            connection.commit();
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed update identity store", e);
-        }
-    }
-
-    public void deleteIdentity(final RecipientId recipientId) {
-        try (final var connection = database.getConnection()) {
-            deleteIdentity(connection, recipientId);
+            deleteIdentity(connection, serviceId);
         } catch (SQLException e) {
             throw new RuntimeException("Failed update identity store", e);
         }
@@ -214,49 +184,49 @@ public class IdentityKeyStore {
     }
 
     private IdentityInfo loadIdentity(
-            final Connection connection, final RecipientId recipientId
+            final Connection connection, final ServiceId serviceId
     ) throws SQLException {
         final var sql = (
                 """
-                SELECT i.recipient_id, i.identity_key, i.added_timestamp, i.trust_level
+                SELECT i.uuid, i.identity_key, i.added_timestamp, i.trust_level
                 FROM %s AS i
-                WHERE i.recipient_id = ?
+                WHERE i.uuid = ?
                 """
         ).formatted(TABLE_IDENTITY);
         try (final var statement = connection.prepareStatement(sql)) {
-            statement.setLong(1, recipientId.id());
+            statement.setBytes(1, serviceId.toByteArray());
             return Utils.executeQueryForOptional(statement, this::getIdentityInfoFromResultSet).orElse(null);
         }
     }
 
     private void saveNewIdentity(
             final Connection connection,
-            final RecipientId recipientId,
+            final ServiceId serviceId,
             final IdentityKey identityKey,
             final boolean firstIdentity
     ) throws SQLException {
         final var trustLevel = trustNewIdentity == TrustNewIdentity.ALWAYS || (
                 trustNewIdentity == TrustNewIdentity.ON_FIRST_USE && firstIdentity
         ) ? TrustLevel.TRUSTED_UNVERIFIED : TrustLevel.UNTRUSTED;
-        logger.debug("Storing new identity for recipient {} with trust {}", recipientId, trustLevel);
-        final var newIdentityInfo = new IdentityInfo(recipientId, identityKey, trustLevel, System.currentTimeMillis());
+        logger.debug("Storing new identity for recipient {} with trust {}", serviceId, trustLevel);
+        final var newIdentityInfo = new IdentityInfo(serviceId, identityKey, trustLevel, System.currentTimeMillis());
         storeIdentity(connection, newIdentityInfo);
-        identityChanges.onNext(recipientId);
+        identityChanges.onNext(serviceId);
     }
 
     private void storeIdentity(final Connection connection, final IdentityInfo identityInfo) throws SQLException {
         logger.trace("Storing identity info for {}, trust: {}, added: {}",
-                identityInfo.getRecipientId(),
+                identityInfo.getServiceId(),
                 identityInfo.getTrustLevel(),
                 identityInfo.getDateAddedTimestamp());
         final var sql = (
                 """
-                INSERT OR REPLACE INTO %s (recipient_id, identity_key, added_timestamp, trust_level)
+                INSERT OR REPLACE INTO %s (uuid, identity_key, added_timestamp, trust_level)
                 VALUES (?, ?, ?, ?)
                 """
         ).formatted(TABLE_IDENTITY);
         try (final var statement = connection.prepareStatement(sql)) {
-            statement.setLong(1, identityInfo.getRecipientId().id());
+            statement.setBytes(1, identityInfo.getServiceId().toByteArray());
             statement.setBytes(2, identityInfo.getIdentityKey().serialize());
             statement.setLong(3, identityInfo.getDateAddedTimestamp());
             statement.setInt(4, identityInfo.getTrustLevel().ordinal());
@@ -264,27 +234,27 @@ public class IdentityKeyStore {
         }
     }
 
-    private void deleteIdentity(final Connection connection, final RecipientId recipientId) throws SQLException {
+    private void deleteIdentity(final Connection connection, final ServiceId serviceId) throws SQLException {
         final var sql = (
                 """
                 DELETE FROM %s AS i
-                WHERE i.recipient_id = ?
+                WHERE i.uuid = ?
                 """
         ).formatted(TABLE_IDENTITY);
         try (final var statement = connection.prepareStatement(sql)) {
-            statement.setLong(1, recipientId.id());
+            statement.setBytes(1, serviceId.toByteArray());
             statement.executeUpdate();
         }
     }
 
     private IdentityInfo getIdentityInfoFromResultSet(ResultSet resultSet) throws SQLException {
         try {
-            final var recipientId = recipientIdCreator.create(resultSet.getLong("recipient_id"));
+            final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
             final var id = new IdentityKey(resultSet.getBytes("identity_key"));
             final var trustLevel = TrustLevel.fromInt(resultSet.getInt("trust_level"));
             final var added = resultSet.getLong("added_timestamp");
 
-            return new IdentityInfo(recipientId, id, trustLevel, added);
+            return new IdentityInfo(serviceId, id, trustLevel, added);
         } catch (InvalidKeyException e) {
             logger.warn("Failed to load identity key, resetting: {}", e.getMessage());
             return null;
index 669602e52848418ffd975dfaf97b02ce693c417c..aa800a042bc93f0662e2567b8e6fb0bd14bd055a 100644 (file)
@@ -3,6 +3,7 @@ package org.asamk.signal.manager.storage.identities;
 import com.fasterxml.jackson.databind.ObjectMapper;
 
 import org.asamk.signal.manager.api.TrustLevel;
+import org.asamk.signal.manager.helper.RecipientAddressResolver;
 import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.asamk.signal.manager.util.IOUtils;
@@ -27,16 +28,21 @@ public class LegacyIdentityKeyStore {
     private static final ObjectMapper objectMapper = org.asamk.signal.manager.storage.Utils.createStorageObjectMapper();
 
     public static void migrate(
-            final File identitiesPath, final RecipientResolver resolver, final IdentityKeyStore identityKeyStore
+            final File identitiesPath,
+            final RecipientResolver resolver,
+            final RecipientAddressResolver addressResolver,
+            final IdentityKeyStore identityKeyStore
     ) {
-        final var identities = getIdentities(identitiesPath, resolver);
+        final var identities = getIdentities(identitiesPath, resolver, addressResolver);
         identityKeyStore.addLegacyIdentities(identities);
         removeIdentityFiles(identitiesPath);
     }
 
     static final Pattern identityFileNamePattern = Pattern.compile("(\\d+)");
 
-    private static List<IdentityInfo> getIdentities(final File identitiesPath, final RecipientResolver resolver) {
+    private static List<IdentityInfo> getIdentities(
+            final File identitiesPath, final RecipientResolver resolver, final RecipientAddressResolver addressResolver
+    ) {
         final var files = identitiesPath.listFiles();
         if (files == null) {
             return List.of();
@@ -45,7 +51,7 @@ public class LegacyIdentityKeyStore {
                 .filter(f -> identityFileNamePattern.matcher(f.getName()).matches())
                 .map(f -> resolver.resolveRecipient(Long.parseLong(f.getName())))
                 .filter(Objects::nonNull)
-                .map(recipientId -> loadIdentityLocked(recipientId, identitiesPath))
+                .map(recipientId -> loadIdentityLocked(recipientId, addressResolver, identitiesPath))
                 .filter(Objects::nonNull)
                 .toList();
     }
@@ -59,7 +65,9 @@ public class LegacyIdentityKeyStore {
         return new File(identitiesPath, String.valueOf(recipientId.id()));
     }
 
-    private static IdentityInfo loadIdentityLocked(final RecipientId recipientId, final File identitiesPath) {
+    private static IdentityInfo loadIdentityLocked(
+            final RecipientId recipientId, RecipientAddressResolver addressResolver, final File identitiesPath
+    ) {
         final var file = getIdentityFile(recipientId, identitiesPath);
         if (!file.exists()) {
             return null;
@@ -71,7 +79,8 @@ public class LegacyIdentityKeyStore {
             var trustLevel = TrustLevel.fromInt(storage.trustLevel());
             var added = storage.addedTimestamp();
 
-            return new IdentityInfo(recipientId, id, trustLevel, added);
+            final var serviceId = addressResolver.resolveRecipientAddress(recipientId).getServiceId();
+            return new IdentityInfo(serviceId, id, trustLevel, added);
         } catch (IOException | InvalidKeyException e) {
             logger.warn("Failed to load identity key: {}", e.getMessage());
             return null;
index c15b858e1e254ac454ef458a702b07f80a7e2139..66dfec4e24f800bd2e9fe9bfe3910edbfe572084 100644 (file)
@@ -1,16 +1,15 @@
 package org.asamk.signal.manager.storage.identities;
 
-import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.IdentityKey;
 import org.signal.libsignal.protocol.IdentityKeyPair;
 import org.signal.libsignal.protocol.SignalProtocolAddress;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.util.function.Supplier;
 
 public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.state.IdentityKeyStore {
 
-    private final RecipientResolver resolver;
     private final Supplier<IdentityKeyPair> identityKeyPairSupplier;
     private final int localRegistrationId;
     private final IdentityKeyStore identityKeyStore;
@@ -21,7 +20,6 @@ public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.sta
             final int localRegistrationId,
             final IdentityKeyStore identityKeyStore
     ) {
-        this.resolver = resolver;
         this.identityKeyPairSupplier = identityKeyPairSupplier;
         this.localRegistrationId = localRegistrationId;
         this.identityKeyStore = identityKeyStore;
@@ -39,29 +37,22 @@ public class SignalIdentityKeyStore implements org.signal.libsignal.protocol.sta
 
     @Override
     public boolean saveIdentity(SignalProtocolAddress address, IdentityKey identityKey) {
-        final var recipientId = resolveRecipient(address.getName());
+        final var serviceId = ServiceId.parseOrThrow(address.getName());
 
-        return identityKeyStore.saveIdentity(recipientId, identityKey);
+        return identityKeyStore.saveIdentity(serviceId, identityKey);
     }
 
     @Override
     public boolean isTrustedIdentity(SignalProtocolAddress address, IdentityKey identityKey, Direction direction) {
-        var recipientId = resolveRecipient(address.getName());
+        final var serviceId = ServiceId.parseOrThrow(address.getName());
 
-        return identityKeyStore.isTrustedIdentity(recipientId, identityKey, direction);
+        return identityKeyStore.isTrustedIdentity(serviceId, identityKey, direction);
     }
 
     @Override
     public IdentityKey getIdentity(SignalProtocolAddress address) {
-        var recipientId = resolveRecipient(address.getName());
-        final var identityInfo = identityKeyStore.getIdentityInfo(recipientId);
+        final var serviceId = ServiceId.parseOrThrow(address.getName());
+        final var identityInfo = identityKeyStore.getIdentityInfo(serviceId);
         return identityInfo == null ? null : identityInfo.getIdentityKey();
     }
-
-    /**
-     * @param identifier can be either a serialized uuid or an e164 phone number
-     */
-    private RecipientId resolveRecipient(String identifier) {
-        return resolver.resolveRecipient(identifier);
-    }
 }
index 0ab8c8f5ac3e52646c461ffc50d0603e2aea4cad..c5f0ccbce26ac57d6cc71c404934b2068eb484b4 100644 (file)
@@ -35,6 +35,10 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
         this(Optional.of(uuid), Optional.empty());
     }
 
+    public ServiceId getServiceId() {
+        return ServiceId.from(uuid.orElse(UNKNOWN_UUID));
+    }
+
     public String getIdentifier() {
         if (uuid.isPresent()) {
             return uuid.get().toString();
@@ -62,6 +66,6 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number) {
     }
 
     public SignalServiceAddress toSignalServiceAddress() {
-        return new SignalServiceAddress(ServiceId.from(uuid.orElse(UNKNOWN_UUID)), number);
+        return new SignalServiceAddress(getServiceId(), number);
     }
 }
index 54632395e1eb0fced1f3aac2776d301cec01e087..c7e6cdb18c2eed250f4b62f8a6e8af6af7eeb818 100644 (file)
@@ -4,14 +4,13 @@ import org.asamk.signal.manager.groups.GroupId;
 import org.asamk.signal.manager.groups.GroupUtils;
 import org.asamk.signal.manager.storage.Database;
 import org.asamk.signal.manager.storage.Utils;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.zkgroup.InvalidInputException;
 import org.signal.libsignal.zkgroup.groups.GroupMasterKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.whispersystems.signalservice.api.crypto.ContentHint;
 import org.whispersystems.signalservice.api.messages.SendMessageResult;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.internal.push.SignalServiceProtos;
 
 import java.io.IOException;
@@ -32,14 +31,10 @@ public class MessageSendLogStore implements AutoCloseable {
 
     private static final Duration LOG_DURATION = Duration.ofDays(1);
 
-    private final RecipientResolver recipientResolver;
     private final Database database;
     private final Thread cleanupThread;
 
-    public MessageSendLogStore(
-            final RecipientResolver recipientResolver, final Database database
-    ) {
-        this.recipientResolver = recipientResolver;
+    public MessageSendLogStore(final Database database) {
         this.database = database;
         this.cleanupThread = new Thread(() -> {
             try {
@@ -69,9 +64,9 @@ public class MessageSendLogStore implements AutoCloseable {
                                     CREATE TABLE message_send_log (
                                       _id INTEGER PRIMARY KEY,
                                       content_id INTEGER NOT NULL REFERENCES message_send_log_content (_id) ON DELETE CASCADE,
-                                      recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
+                                      uuid BLOB NOT NULL,
                                       device_id INTEGER NOT NULL
-                                    );
+                                    ) STRICT;
                                     CREATE TABLE message_send_log_content (
                                       _id INTEGER PRIMARY KEY,
                                       group_id BLOB,
@@ -81,26 +76,26 @@ public class MessageSendLogStore implements AutoCloseable {
                                       urgent BOOLEAN NOT NULL
                                     );
                                     CREATE INDEX mslc_timestamp_index ON message_send_log_content (timestamp);
-                                    CREATE INDEX msl_recipient_index ON message_send_log (recipient_id, device_id, content_id);
+                                    CREATE INDEX msl_recipient_index ON message_send_log (uuid, device_id, content_id);
                                     CREATE INDEX msl_content_index ON message_send_log (content_id);
                                     """);
         }
     }
 
     public List<MessageSendLogEntry> findMessages(
-            final RecipientId recipientId, final int deviceId, final long timestamp, final boolean isSenderKey
+            final ServiceId serviceId, final int deviceId, final long timestamp, final boolean isSenderKey
     ) {
         final var sql = """
                         SELECT group_id, content, content_hint
                         FROM %s l
                              INNER JOIN %s lc ON l.content_id = lc._id
-                        WHERE l.recipient_id = ? AND l.device_id = ? AND lc.timestamp = ?
+                        WHERE l.uuid = ? AND l.device_id = ? AND lc.timestamp = ?
                         """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT);
         try (final var connection = database.getConnection()) {
             deleteOutdatedEntries(connection);
 
             try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
+                statement.setBytes(1, serviceId.toByteArray());
                 statement.setInt(2, deviceId);
                 statement.setLong(3, timestamp);
                 try (var result = Utils.executeQueryForStream(statement, this::getMessageSendLogEntryFromResultSet)) {
@@ -189,16 +184,16 @@ public class MessageSendLogStore implements AutoCloseable {
         }
     }
 
-    public void deleteEntryForRecipientNonGroup(long sentTimestamp, RecipientId recipientId) {
+    public void deleteEntryForRecipientNonGroup(long sentTimestamp, ServiceId serviceId) {
         final var sql = """
                         DELETE FROM %s AS lc
-                        WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.recipient_id = ?)
+                        WHERE lc.timestamp = ? AND lc.group_id IS NULL AND lc._id IN (SELECT content_id FROM %s l WHERE l.uuid = ?)
                         """.formatted(TABLE_MESSAGE_SEND_LOG_CONTENT, TABLE_MESSAGE_SEND_LOG);
         try (final var connection = database.getConnection()) {
             connection.setAutoCommit(false);
             try (final var statement = connection.prepareStatement(sql)) {
                 statement.setLong(1, sentTimestamp);
-                statement.setLong(2, recipientId.id());
+                statement.setBytes(2, serviceId.toByteArray());
                 statement.executeUpdate();
             }
 
@@ -209,21 +204,21 @@ public class MessageSendLogStore implements AutoCloseable {
         }
     }
 
-    public void deleteEntryForRecipient(long sentTimestamp, RecipientId recipientId, int deviceId) {
-        deleteEntriesForRecipient(List.of(sentTimestamp), recipientId, deviceId);
+    public void deleteEntryForRecipient(long sentTimestamp, ServiceId serviceId, int deviceId) {
+        deleteEntriesForRecipient(List.of(sentTimestamp), serviceId, deviceId);
     }
 
-    public void deleteEntriesForRecipient(List<Long> sentTimestamps, RecipientId recipientId, int deviceId) {
+    public void deleteEntriesForRecipient(List<Long> sentTimestamps, ServiceId serviceId, int deviceId) {
         final var sql = """
                         DELETE FROM %s AS l
-                        WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.recipient_id = ? AND l.device_id = ?
+                        WHERE l.content_id IN (SELECT _id FROM %s lc WHERE lc.timestamp = ?) AND l.uuid = ? AND l.device_id = ?
                         """.formatted(TABLE_MESSAGE_SEND_LOG, TABLE_MESSAGE_SEND_LOG_CONTENT);
         try (final var connection = database.getConnection()) {
             connection.setAutoCommit(false);
             try (final var statement = connection.prepareStatement(sql)) {
                 for (final var sentTimestamp : sentTimestamps) {
                     statement.setLong(1, sentTimestamp);
-                    statement.setLong(2, recipientId.id());
+                    statement.setBytes(2, serviceId.toByteArray());
                     statement.setInt(3, deviceId);
                     statement.executeUpdate();
                 }
@@ -247,8 +242,8 @@ public class MessageSendLogStore implements AutoCloseable {
 
     private RecipientDevices getRecipientDevices(final SendMessageResult sendMessageResult) {
         if (sendMessageResult.isSuccess() && sendMessageResult.getSuccess().getContent().isPresent()) {
-            final var recipientId = recipientResolver.resolveRecipient(sendMessageResult.getAddress());
-            return new RecipientDevices(recipientId, sendMessageResult.getSuccess().getDevices());
+            final var serviceId = sendMessageResult.getAddress().getServiceId();
+            return new RecipientDevices(serviceId, sendMessageResult.getSuccess().getDevices());
         } else {
             return null;
         }
@@ -332,13 +327,13 @@ public class MessageSendLogStore implements AutoCloseable {
             final long contentId, final List<RecipientDevices> recipientDevices, final Connection connection
     ) throws SQLException {
         final var sql = """
-                        INSERT INTO %s (recipient_id, device_id, content_id)
+                        INSERT INTO %s (uuid, device_id, content_id)
                         VALUES (?,?,?)
                         """.formatted(TABLE_MESSAGE_SEND_LOG);
         try (final var statement = connection.prepareStatement(sql)) {
             for (final var recipientDevice : recipientDevices) {
                 for (final var deviceId : recipientDevice.deviceIds()) {
-                    statement.setLong(1, recipientDevice.recipientId().id());
+                    statement.setBytes(1, recipientDevice.serviceId().toByteArray());
                     statement.setInt(2, deviceId);
                     statement.setLong(3, contentId);
                     statement.executeUpdate();
@@ -387,5 +382,5 @@ public class MessageSendLogStore implements AutoCloseable {
         return new MessageSendLogEntry(groupId, content, contentHint, urgent);
     }
 
-    private record RecipientDevices(RecipientId recipientId, List<Integer> deviceIds) {}
+    private record RecipientDevices(ServiceId serviceId, List<Integer> deviceIds) {}
 }
index ea823cb28cbb280b5bf5efc80632478e7429b8d4..f5a1fc53e11f8506385b46826100d0f6c1469542 100644 (file)
@@ -1,11 +1,14 @@
 package org.asamk.signal.manager.storage.senderKeys;
 
 import org.asamk.signal.manager.api.Pair;
+import org.asamk.signal.manager.helper.RecipientAddressResolver;
+import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.InvalidMessageException;
 import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -18,14 +21,15 @@ import java.util.UUID;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import static org.asamk.signal.manager.storage.senderKeys.SenderKeyRecordStore.Key;
-
 public class LegacySenderKeyRecordStore {
 
     private final static Logger logger = LoggerFactory.getLogger(LegacySenderKeyRecordStore.class);
 
     public static void migrate(
-            final File senderKeysPath, final RecipientResolver resolver, SenderKeyStore senderKeyStore
+            final File senderKeysPath,
+            final RecipientResolver resolver,
+            final RecipientAddressResolver addressResolver,
+            final SenderKeyStore senderKeyStore
     ) {
         final var files = senderKeysPath.listFiles();
         if (files == null) {
@@ -34,10 +38,13 @@ public class LegacySenderKeyRecordStore {
 
         final var senderKeys = parseFileNames(files, resolver).stream().map(key -> {
             final var record = loadSenderKeyLocked(key, senderKeysPath);
-            if (record == null) {
+            final var uuid = addressResolver.resolveRecipientAddress(key.recipientId).uuid();
+            if (record == null || uuid.isEmpty()) {
                 return null;
             }
-            return new Pair<>(key, record);
+            return new Pair<>(new SenderKeyRecordStore.Key(ServiceId.from(uuid.get()),
+                    key.deviceId,
+                    key.distributionId), record);
         }).filter(Objects::nonNull).toList();
 
         senderKeyStore.addLegacySenderKeys(senderKeys);
@@ -98,4 +105,6 @@ public class LegacySenderKeyRecordStore {
             return null;
         }
     }
+
+    record Key(RecipientId recipientId, int deviceId, UUID distributionId) {}
 }
index 87c298f405a1f94694bb4fe10bca420cffd20550..47465b3e4ff7cae86b7e36db9ca88f9c8b7bd6bf 100644 (file)
@@ -1,11 +1,13 @@
 package org.asamk.signal.manager.storage.senderKeys;
 
+import org.asamk.signal.manager.helper.RecipientAddressResolver;
 import org.asamk.signal.manager.storage.Utils;
 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.asamk.signal.manager.storage.senderKeys.SenderKeySharedStore.SenderKeySharedEntry;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.whispersystems.signalservice.api.push.DistributionId;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -21,7 +23,10 @@ public class LegacySenderKeySharedStore {
     private final static Logger logger = LoggerFactory.getLogger(LegacySenderKeySharedStore.class);
 
     public static void migrate(
-            final File file, final RecipientResolver resolver, SenderKeyStore senderKeyStore
+            final File file,
+            final RecipientResolver resolver,
+            final RecipientAddressResolver addressResolver,
+            final SenderKeyStore senderKeyStore
     ) {
         final var objectMapper = Utils.createStorageObjectMapper();
         try (var inputStream = new FileInputStream(file)) {
@@ -32,7 +37,11 @@ public class LegacySenderKeySharedStore {
                 if (recipientId == null) {
                     continue;
                 }
-                final var entry = new SenderKeySharedEntry(recipientId, senderKey.deviceId);
+                final var uuid = addressResolver.resolveRecipientAddress(recipientId).uuid();
+                if (uuid.isEmpty()) {
+                    continue;
+                }
+                final var entry = new SenderKeySharedEntry(ServiceId.from(uuid.get()), senderKey.deviceId);
                 final var distributionId = DistributionId.from(senderKey.distributionId);
                 var entries = sharedSenderKeys.get(distributionId);
                 if (entries == null) {
index 9b612aac0043ad81fb7ba843e94253c72591f32a..5e42a924ff36eb8fb5bf206438fe5092350bb624 100644 (file)
@@ -3,14 +3,13 @@ package org.asamk.signal.manager.storage.senderKeys;
 import org.asamk.signal.manager.api.Pair;
 import org.asamk.signal.manager.storage.Database;
 import org.asamk.signal.manager.storage.Utils;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.InvalidMessageException;
 import org.signal.libsignal.protocol.SignalProtocolAddress;
 import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
 import org.signal.libsignal.protocol.groups.state.SenderKeyStore;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.api.util.UuidUtil;
 
 import java.sql.Connection;
@@ -25,7 +24,6 @@ public class SenderKeyRecordStore implements SenderKeyStore {
     private final static String TABLE_SENDER_KEY = "sender_key";
 
     private final Database database;
-    private final RecipientResolver resolver;
 
     public static void createSql(Connection connection) throws SQLException {
         // When modifying the CREATE statement here, also add a migration in AccountDatabase.java
@@ -33,22 +31,19 @@ public class SenderKeyRecordStore implements SenderKeyStore {
             statement.executeUpdate("""
                                     CREATE TABLE sender_key (
                                       _id INTEGER PRIMARY KEY,
-                                      recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
+                                      uuid BLOB NOT NULL,
                                       device_id INTEGER NOT NULL,
                                       distribution_id BLOB NOT NULL,
                                       record BLOB NOT NULL,
                                       created_timestamp INTEGER NOT NULL,
-                                      UNIQUE(recipient_id, device_id, distribution_id)
-                                    );
+                                      UNIQUE(uuid, device_id, distribution_id)
+                                    ) STRICT;
                                     """);
         }
     }
 
-    SenderKeyRecordStore(
-            final Database database, final RecipientResolver resolver
-    ) {
+    SenderKeyRecordStore(final Database database) {
         this.database = database;
-        this.resolver = resolver;
     }
 
     @Override
@@ -75,17 +70,17 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         }
     }
 
-    long getCreateTimeForKey(final RecipientId selfRecipientId, final int selfDeviceId, final UUID distributionId) {
+    long getCreateTimeForKey(final ServiceId selfServiceId, final int selfDeviceId, final UUID distributionId) {
         final var sql = (
                 """
                 SELECT s.created_timestamp
                 FROM %s AS s
-                WHERE s.recipient_id = ? AND s.device_id = ? AND s.distribution_id = ?
+                WHERE s.uuid = ? AND s.device_id = ? AND s.distribution_id = ?
                 """
         ).formatted(TABLE_SENDER_KEY);
         try (final var connection = database.getConnection()) {
             try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, selfRecipientId.id());
+                statement.setBytes(1, selfServiceId.toByteArray());
                 statement.setInt(2, selfDeviceId);
                 statement.setBytes(3, UuidUtil.toByteArray(distributionId));
                 return Utils.executeQueryForOptional(statement, res -> res.getLong("created_timestamp")).orElse(-1L);
@@ -95,16 +90,16 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         }
     }
 
-    void deleteSenderKey(final RecipientId recipientId, final UUID distributionId) {
+    void deleteSenderKey(final ServiceId serviceId, final UUID distributionId) {
         final var sql = (
                 """
                 DELETE FROM %s AS s
-                WHERE s.recipient_id = ? AND s.distribution_id = ?
+                WHERE s.uuid = ? AND s.distribution_id = ?
                 """
         ).formatted(TABLE_SENDER_KEY);
         try (final var connection = database.getConnection()) {
             try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
+                statement.setBytes(1, serviceId.toByteArray());
                 statement.setBytes(2, UuidUtil.toByteArray(distributionId));
                 statement.executeUpdate();
             }
@@ -126,33 +121,9 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         }
     }
 
-    void deleteAllFor(final RecipientId recipientId) {
+    void deleteAllFor(final ServiceId serviceId) {
         try (final var connection = database.getConnection()) {
-            deleteAllFor(connection, recipientId);
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed update sender key store", e);
-        }
-    }
-
-    void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
-        try (final var connection = database.getConnection()) {
-            connection.setAutoCommit(false);
-            final var sql = """
-                            UPDATE OR IGNORE %s
-                            SET recipient_id = ?
-                            WHERE recipient_id = ?
-                            """.formatted(TABLE_SENDER_KEY);
-            try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
-                statement.setLong(2, toBeMergedRecipientId.id());
-                final var rows = statement.executeUpdate();
-                if (rows > 0) {
-                    logger.debug("Reassigned {} sender keys of to be merged recipient.", rows);
-                }
-            }
-            // Delete all conflicting sender keys now
-            deleteAllFor(connection, toBeMergedRecipientId);
-            connection.commit();
+            deleteAllFor(connection, serviceId);
         } catch (SQLException e) {
             throw new RuntimeException("Failed update sender key store", e);
         }
@@ -173,16 +144,9 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         logger.debug("Complete sender keys migration took {}ms", (System.nanoTime() - start) / 1000000);
     }
 
-    /**
-     * @param identifier can be either a serialized uuid or an e164 phone number
-     */
-    private RecipientId resolveRecipient(String identifier) {
-        return resolver.resolveRecipient(identifier);
-    }
-
     private Key getKey(final SignalProtocolAddress address, final UUID distributionId) {
-        final var recipientId = resolveRecipient(address.getName());
-        return new Key(recipientId, address.getDeviceId(), distributionId);
+        final var serviceId = ServiceId.parseOrThrow(address.getName());
+        return new Key(serviceId, address.getDeviceId(), distributionId);
     }
 
     private SenderKeyRecord loadSenderKey(final Connection connection, final Key key) throws SQLException {
@@ -190,11 +154,11 @@ public class SenderKeyRecordStore implements SenderKeyStore {
                 """
                 SELECT s.record
                 FROM %s AS s
-                WHERE s.recipient_id = ? AND s.device_id = ? AND s.distribution_id = ?
+                WHERE s.uuid = ? AND s.device_id = ? AND s.distribution_id = ?
                 """
         ).formatted(TABLE_SENDER_KEY);
         try (final var statement = connection.prepareStatement(sql)) {
-            statement.setLong(1, key.recipientId().id());
+            statement.setBytes(1, key.serviceId().toByteArray());
             statement.setInt(2, key.deviceId());
             statement.setBytes(3, UuidUtil.toByteArray(key.distributionId()));
             return Utils.executeQueryForOptional(statement, this::getSenderKeyRecordFromResultSet).orElse(null);
@@ -207,11 +171,11 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         final var sqlUpdate = """
                               UPDATE %s
                               SET record = ?
-                              WHERE recipient_id = ? AND device_id = ? and distribution_id = ?
+                              WHERE uuid = ? AND device_id = ? and distribution_id = ?
                               """.formatted(TABLE_SENDER_KEY);
         try (final var statement = connection.prepareStatement(sqlUpdate)) {
             statement.setBytes(1, senderKeyRecord.serialize());
-            statement.setLong(2, key.recipientId().id());
+            statement.setBytes(2, key.serviceId().toByteArray());
             statement.setLong(3, key.deviceId());
             statement.setBytes(4, UuidUtil.toByteArray(key.distributionId()));
             final var rows = statement.executeUpdate();
@@ -223,12 +187,12 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         // Record doesn't exist yet, creating a new one
         final var sqlInsert = (
                 """
-                INSERT OR REPLACE INTO %s (recipient_id, device_id, distribution_id, record, created_timestamp)
+                INSERT OR REPLACE INTO %s (uuid, device_id, distribution_id, record, created_timestamp)
                 VALUES (?, ?, ?, ?, ?)
                 """
         ).formatted(TABLE_SENDER_KEY);
         try (final var statement = connection.prepareStatement(sqlInsert)) {
-            statement.setLong(1, key.recipientId().id());
+            statement.setBytes(1, key.serviceId().toByteArray());
             statement.setInt(2, key.deviceId());
             statement.setBytes(3, UuidUtil.toByteArray(key.distributionId()));
             statement.setBytes(4, senderKeyRecord.serialize());
@@ -237,15 +201,15 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         }
     }
 
-    private void deleteAllFor(final Connection connection, final RecipientId recipientId) throws SQLException {
+    private void deleteAllFor(final Connection connection, final ServiceId serviceId) throws SQLException {
         final var sql = (
                 """
                 DELETE FROM %s AS s
-                WHERE s.recipient_id = ?
+                WHERE s.uuid = ?
                 """
         ).formatted(TABLE_SENDER_KEY);
         try (final var statement = connection.prepareStatement(sql)) {
-            statement.setLong(1, recipientId.id());
+            statement.setBytes(1, serviceId.toByteArray());
             statement.executeUpdate();
         }
     }
@@ -261,5 +225,5 @@ public class SenderKeyRecordStore implements SenderKeyStore {
         }
     }
 
-    record Key(RecipientId recipientId, int deviceId, UUID distributionId) {}
+    record Key(ServiceId serviceId, int deviceId, UUID distributionId) {}
 }
index 2eb7c4bc56ae0f0d0cdba04fdfd907bcfb5f1cdd..112c358c8943d14788761bb8fc865281c6863369 100644 (file)
@@ -1,15 +1,12 @@
 package org.asamk.signal.manager.storage.senderKeys;
 
-import org.asamk.signal.manager.helper.RecipientAddressResolver;
 import org.asamk.signal.manager.storage.Database;
 import org.asamk.signal.manager.storage.Utils;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
-import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.SignalProtocolAddress;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.whispersystems.signalservice.api.push.DistributionId;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.api.util.UuidUtil;
 
 import java.sql.Connection;
@@ -26,9 +23,6 @@ public class SenderKeySharedStore {
     private final static String TABLE_SENDER_KEY_SHARED = "sender_key_shared";
 
     private final Database database;
-    private final RecipientIdCreator recipientIdCreator;
-    private final RecipientResolver resolver;
-    private final RecipientAddressResolver addressResolver;
 
     public static void createSql(Connection connection) throws SQLException {
         // When modifying the CREATE statement here, also add a migration in AccountDatabase.java
@@ -36,33 +30,25 @@ public class SenderKeySharedStore {
             statement.executeUpdate("""
                                     CREATE TABLE sender_key_shared (
                                       _id INTEGER PRIMARY KEY,
-                                      recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
+                                      uuid BLOB NOT NULL,
                                       device_id INTEGER NOT NULL,
                                       distribution_id BLOB NOT NULL,
                                       timestamp INTEGER NOT NULL,
-                                      UNIQUE(recipient_id, device_id, distribution_id)
-                                    );
+                                      UNIQUE(uuid, device_id, distribution_id)
+                                    ) STRICT;
                                     """);
         }
     }
 
-    SenderKeySharedStore(
-            final Database database,
-            final RecipientIdCreator recipientIdCreator,
-            final RecipientAddressResolver addressResolver,
-            final RecipientResolver resolver
-    ) {
+    SenderKeySharedStore(final Database database) {
         this.database = database;
-        this.recipientIdCreator = recipientIdCreator;
-        this.addressResolver = addressResolver;
-        this.resolver = resolver;
     }
 
     public Set<SignalProtocolAddress> getSenderKeySharedWith(final DistributionId distributionId) {
         try (final var connection = database.getConnection()) {
             final var sql = (
                     """
-                    SELECT s.recipient_id, s.device_id
+                    SELECT s.uuid, s.device_id
                     FROM %s AS s
                     WHERE s.distribution_id = ?
                     """
@@ -70,8 +56,7 @@ public class SenderKeySharedStore {
             try (final var statement = connection.prepareStatement(sql)) {
                 statement.setBytes(1, UuidUtil.toByteArray(distributionId.asUuid()));
                 return Utils.executeQueryForStream(statement, this::getSenderKeySharedEntryFromResultSet)
-                        .map(k -> new SignalProtocolAddress(addressResolver.resolveRecipientAddress(k.recipientId())
-                                .getIdentifier(), k.deviceId()))
+                        .map(k -> k.serviceId.toProtocolAddress(k.deviceId()))
                         .collect(Collectors.toSet());
             }
         } catch (SQLException e) {
@@ -83,7 +68,7 @@ public class SenderKeySharedStore {
             final DistributionId distributionId, final Collection<SignalProtocolAddress> addresses
     ) {
         final var newEntries = addresses.stream()
-                .map(a -> new SenderKeySharedEntry(resolver.resolveRecipient(a.getName()), a.getDeviceId()))
+                .map(a -> new SenderKeySharedEntry(ServiceId.parseOrThrow(a.getName()), a.getDeviceId()))
                 .collect(Collectors.toSet());
 
         try (final var connection = database.getConnection()) {
@@ -97,7 +82,8 @@ public class SenderKeySharedStore {
 
     public void clearSenderKeySharedWith(final Collection<SignalProtocolAddress> addresses) {
         final var entriesToDelete = addresses.stream()
-                .map(a -> new SenderKeySharedEntry(resolver.resolveRecipient(a.getName()), a.getDeviceId()))
+                .filter(a -> UuidUtil.isUuid(a.getName()))
+                .map(a -> new SenderKeySharedEntry(ServiceId.parseOrThrow(a.getName()), a.getDeviceId()))
                 .collect(Collectors.toSet());
 
         try (final var connection = database.getConnection()) {
@@ -105,12 +91,12 @@ public class SenderKeySharedStore {
             final var sql = (
                     """
                     DELETE FROM %s AS s
-                    WHERE recipient_id = ? AND device_id = ?
+                    WHERE uuid = ? AND device_id = ?
                     """
             ).formatted(TABLE_SENDER_KEY_SHARED);
             try (final var statement = connection.prepareStatement(sql)) {
                 for (final var entry : entriesToDelete) {
-                    statement.setLong(1, entry.recipientId().id());
+                    statement.setBytes(1, entry.serviceId().toByteArray());
                     statement.setInt(2, entry.deviceId());
                     statement.executeUpdate();
                 }
@@ -136,16 +122,16 @@ public class SenderKeySharedStore {
         }
     }
 
-    public void deleteAllFor(final RecipientId recipientId) {
+    public void deleteAllFor(final ServiceId serviceId) {
         try (final var connection = database.getConnection()) {
             final var sql = (
                     """
                     DELETE FROM %s AS s
-                    WHERE recipient_id = ?
+                    WHERE uuid = ?
                     """
             ).formatted(TABLE_SENDER_KEY_SHARED);
             try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
+                statement.setBytes(1, serviceId.toByteArray());
                 statement.executeUpdate();
             }
         } catch (SQLException e) {
@@ -154,17 +140,17 @@ public class SenderKeySharedStore {
     }
 
     public void deleteSharedWith(
-            final RecipientId recipientId, final int deviceId, final DistributionId distributionId
+            final ServiceId serviceId, final int deviceId, final DistributionId distributionId
     ) {
         try (final var connection = database.getConnection()) {
             final var sql = (
                     """
                     DELETE FROM %s AS s
-                    WHERE recipient_id = ? AND device_id = ? AND distribution_id = ?
+                    WHERE uuid = ? AND device_id = ? AND distribution_id = ?
                     """
             ).formatted(TABLE_SENDER_KEY_SHARED);
             try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
+                statement.setBytes(1, serviceId.toByteArray());
                 statement.setInt(2, deviceId);
                 statement.setBytes(3, UuidUtil.toByteArray(distributionId.asUuid()));
                 statement.executeUpdate();
@@ -191,25 +177,6 @@ public class SenderKeySharedStore {
         }
     }
 
-    public void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
-        try (final var connection = database.getConnection()) {
-            final var sql = (
-                    """
-                    UPDATE OR REPLACE %s
-                    SET recipient_id = ?
-                    WHERE recipient_id = ?
-                    """
-            ).formatted(TABLE_SENDER_KEY_SHARED);
-            try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
-                statement.setLong(2, toBeMergedRecipientId.id());
-                statement.executeUpdate();
-            }
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed update shared sender key store", e);
-        }
-    }
-
     void addLegacySenderKeysShared(final Map<DistributionId, Set<SenderKeySharedEntry>> sharedSenderKeys) {
         logger.debug("Migrating legacy sender keys shared to database");
         long start = System.nanoTime();
@@ -230,13 +197,13 @@ public class SenderKeySharedStore {
     ) throws SQLException {
         final var sql = (
                 """
-                INSERT OR REPLACE INTO %s (recipient_id, device_id, distribution_id, timestamp)
+                INSERT OR REPLACE INTO %s (uuid, device_id, distribution_id, timestamp)
                 VALUES (?, ?, ?, ?)
                 """
         ).formatted(TABLE_SENDER_KEY_SHARED);
         try (final var statement = connection.prepareStatement(sql)) {
             for (final var entry : newEntries) {
-                statement.setLong(1, entry.recipientId().id());
+                statement.setBytes(1, entry.serviceId().toByteArray());
                 statement.setInt(2, entry.deviceId());
                 statement.setBytes(3, UuidUtil.toByteArray(distributionId.asUuid()));
                 statement.setLong(4, System.currentTimeMillis());
@@ -246,10 +213,10 @@ public class SenderKeySharedStore {
     }
 
     private SenderKeySharedEntry getSenderKeySharedEntryFromResultSet(ResultSet resultSet) throws SQLException {
-        final var recipientId = resultSet.getLong("recipient_id");
+        final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
         final var deviceId = resultSet.getInt("device_id");
-        return new SenderKeySharedEntry(recipientIdCreator.create(recipientId), deviceId);
+        return new SenderKeySharedEntry(serviceId, deviceId);
     }
 
-    record SenderKeySharedEntry(RecipientId recipientId, int deviceId) {}
+    record SenderKeySharedEntry(ServiceId serviceId, int deviceId) {}
 }
index 6a3da47e15eed14e8a1724faa2a025a7ff7523c7..d60249b6db6d21f6d0eb1bf5ce0a2c8249c41c05 100644 (file)
@@ -1,15 +1,12 @@
 package org.asamk.signal.manager.storage.senderKeys;
 
 import org.asamk.signal.manager.api.Pair;
-import org.asamk.signal.manager.helper.RecipientAddressResolver;
 import org.asamk.signal.manager.storage.Database;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
-import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.SignalProtocolAddress;
 import org.signal.libsignal.protocol.groups.state.SenderKeyRecord;
 import org.whispersystems.signalservice.api.SignalServiceSenderKeyStore;
 import org.whispersystems.signalservice.api.push.DistributionId;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.util.Collection;
 import java.util.Map;
@@ -21,14 +18,9 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore {
     private final SenderKeyRecordStore senderKeyRecordStore;
     private final SenderKeySharedStore senderKeySharedStore;
 
-    public SenderKeyStore(
-            final Database database,
-            final RecipientAddressResolver addressResolver,
-            final RecipientResolver resolver,
-            final RecipientIdCreator recipientIdCreator
-    ) {
-        this.senderKeyRecordStore = new SenderKeyRecordStore(database, resolver);
-        this.senderKeySharedStore = new SenderKeySharedStore(database, recipientIdCreator, addressResolver, resolver);
+    public SenderKeyStore(final Database database) {
+        this.senderKeyRecordStore = new SenderKeyRecordStore(database);
+        this.senderKeySharedStore = new SenderKeySharedStore(database);
     }
 
     @Override
@@ -65,31 +57,26 @@ public class SenderKeyStore implements SignalServiceSenderKeyStore {
         senderKeyRecordStore.deleteAll();
     }
 
-    public void deleteAll(RecipientId recipientId) {
-        senderKeySharedStore.deleteAllFor(recipientId);
-        senderKeyRecordStore.deleteAllFor(recipientId);
+    public void deleteAll(ServiceId serviceId) {
+        senderKeySharedStore.deleteAllFor(serviceId);
+        senderKeyRecordStore.deleteAllFor(serviceId);
     }
 
-    public void deleteSharedWith(RecipientId recipientId) {
-        senderKeySharedStore.deleteAllFor(recipientId);
+    public void deleteSharedWith(ServiceId serviceId) {
+        senderKeySharedStore.deleteAllFor(serviceId);
     }
 
-    public void deleteSharedWith(RecipientId recipientId, int deviceId, DistributionId distributionId) {
-        senderKeySharedStore.deleteSharedWith(recipientId, deviceId, distributionId);
+    public void deleteSharedWith(ServiceId serviceId, int deviceId, DistributionId distributionId) {
+        senderKeySharedStore.deleteSharedWith(serviceId, deviceId, distributionId);
     }
 
-    public void deleteOurKey(RecipientId selfRecipientId, DistributionId distributionId) {
+    public void deleteOurKey(ServiceId selfServiceId, DistributionId distributionId) {
         senderKeySharedStore.deleteAllFor(distributionId);
-        senderKeyRecordStore.deleteSenderKey(selfRecipientId, distributionId.asUuid());
-    }
-
-    public long getCreateTimeForOurKey(RecipientId selfRecipientId, int deviceId, DistributionId distributionId) {
-        return senderKeyRecordStore.getCreateTimeForKey(selfRecipientId, deviceId, distributionId.asUuid());
+        senderKeyRecordStore.deleteSenderKey(selfServiceId, distributionId.asUuid());
     }
 
-    public void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
-        senderKeySharedStore.mergeRecipients(recipientId, toBeMergedRecipientId);
-        senderKeyRecordStore.mergeRecipients(recipientId, toBeMergedRecipientId);
+    public long getCreateTimeForOurKey(ServiceId selfServiceId, int deviceId, DistributionId distributionId) {
+        return senderKeyRecordStore.getCreateTimeForKey(selfServiceId, deviceId, distributionId.asUuid());
     }
 
     void addLegacySenderKeys(final Collection<Pair<SenderKeyRecordStore.Key, SenderKeyRecord>> senderKeys) {
index d0646ec14340a968bce478fd00e5baa3217c0f55..1682b71472ed8c756359a1bfb1cfe0b99a4f99bd 100644 (file)
@@ -1,12 +1,14 @@
 package org.asamk.signal.manager.storage.sessions;
 
 import org.asamk.signal.manager.api.Pair;
+import org.asamk.signal.manager.helper.RecipientAddressResolver;
+import org.asamk.signal.manager.storage.recipients.RecipientId;
 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
-import org.asamk.signal.manager.storage.sessions.SessionStore.Key;
 import org.asamk.signal.manager.util.IOUtils;
 import org.signal.libsignal.protocol.state.SessionRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.whispersystems.signalservice.api.push.ServiceId;
 
 import java.io.File;
 import java.io.FileInputStream;
@@ -24,15 +26,19 @@ public class LegacySessionStore {
     private final static Logger logger = LoggerFactory.getLogger(LegacySessionStore.class);
 
     public static void migrate(
-            final File sessionsPath, final RecipientResolver resolver, final SessionStore sessionStore
+            final File sessionsPath,
+            final RecipientResolver resolver,
+            final RecipientAddressResolver addressResolver,
+            final SessionStore sessionStore
     ) {
         final var keys = getKeysLocked(sessionsPath, resolver);
         final var sessions = keys.stream().map(key -> {
             final var record = loadSessionLocked(key, sessionsPath);
-            if (record == null) {
+            final var uuid = addressResolver.resolveRecipientAddress(key.recipientId).uuid();
+            if (record == null || uuid.isEmpty()) {
                 return null;
             }
-            return new Pair<>(key, record);
+            return new Pair<>(new SessionStore.Key(ServiceId.from(uuid.get()), key.deviceId()), record);
         }).filter(Objects::nonNull).toList();
         sessionStore.addLegacySessions(sessions);
         deleteAllSessions(sessionsPath);
@@ -104,4 +110,6 @@ public class LegacySessionStore {
             return null;
         }
     }
+
+    record Key(RecipientId recipientId, int deviceId) {}
 }
index ad0e198ea6b03b13f431766e65eed004c8cd2d30..4bac3bd57ffdf22a396b81140e1fd3c6b78a5769 100644 (file)
@@ -3,9 +3,6 @@ package org.asamk.signal.manager.storage.sessions;
 import org.asamk.signal.manager.api.Pair;
 import org.asamk.signal.manager.storage.Database;
 import org.asamk.signal.manager.storage.Utils;
-import org.asamk.signal.manager.storage.recipients.RecipientId;
-import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
-import org.asamk.signal.manager.storage.recipients.RecipientResolver;
 import org.signal.libsignal.protocol.InvalidMessageException;
 import org.signal.libsignal.protocol.NoSessionException;
 import org.signal.libsignal.protocol.SignalProtocolAddress;
@@ -15,7 +12,9 @@ import org.signal.libsignal.protocol.state.SessionRecord;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.whispersystems.signalservice.api.SignalServiceSessionStore;
+import org.whispersystems.signalservice.api.push.ServiceId;
 import org.whispersystems.signalservice.api.push.ServiceIdType;
+import org.whispersystems.signalservice.api.util.UuidUtil;
 
 import java.sql.Connection;
 import java.sql.ResultSet;
@@ -38,8 +37,6 @@ public class SessionStore implements SignalServiceSessionStore {
 
     private final Database database;
     private final int accountIdType;
-    private final RecipientResolver resolver;
-    private final RecipientIdCreator recipientIdCreator;
 
     public static void createSql(Connection connection) throws SQLException {
         // When modifying the CREATE statement here, also add a migration in AccountDatabase.java
@@ -48,25 +45,18 @@ public class SessionStore implements SignalServiceSessionStore {
                                     CREATE TABLE session (
                                       _id INTEGER PRIMARY KEY,
                                       account_id_type INTEGER NOT NULL,
-                                      recipient_id INTEGER NOT NULL REFERENCES recipient (_id) ON DELETE CASCADE,
+                                      uuid BLOB NOT NULL,
                                       device_id INTEGER NOT NULL,
                                       record BLOB NOT NULL,
-                                      UNIQUE(account_id_type, recipient_id, device_id)
-                                    );
+                                      UNIQUE(account_id_type, uuid, device_id)
+                                    ) STRICT;
                                     """);
         }
     }
 
-    public SessionStore(
-            final Database database,
-            final ServiceIdType serviceIdType,
-            final RecipientResolver resolver,
-            final RecipientIdCreator recipientIdCreator
-    ) {
+    public SessionStore(final Database database, final ServiceIdType serviceIdType) {
         this.database = database;
         this.accountIdType = Utils.getAccountIdType(serviceIdType);
-        this.resolver = resolver;
-        this.recipientIdCreator = recipientIdCreator;
     }
 
     @Override
@@ -111,19 +101,19 @@ public class SessionStore implements SignalServiceSessionStore {
 
     @Override
     public List<Integer> getSubDeviceSessions(String name) {
-        final var recipientId = resolver.resolveRecipient(name);
+        final var serviceId = ServiceId.parseOrThrow(name);
         // get all sessions for recipient except primary device session
         final var sql = (
                 """
                 SELECT s.device_id
                 FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id = ? AND s.device_id != 1
+                WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id != 1
                 """
         ).formatted(TABLE_SESSION);
         try (final var connection = database.getConnection()) {
             try (final var statement = connection.prepareStatement(sql)) {
                 statement.setInt(1, accountIdType);
-                statement.setLong(2, recipientId.id());
+                statement.setBytes(2, serviceId.toByteArray());
                 return Utils.executeQueryForStream(statement, res -> res.getInt("device_id")).toList();
             }
         } catch (SQLException e) {
@@ -131,8 +121,8 @@ public class SessionStore implements SignalServiceSessionStore {
         }
     }
 
-    public boolean isCurrentRatchetKey(RecipientId recipientId, int deviceId, ECPublicKey ratchetKey) {
-        final var key = new Key(recipientId, deviceId);
+    public boolean isCurrentRatchetKey(ServiceId serviceId, int deviceId, ECPublicKey ratchetKey) {
+        final var key = new Key(serviceId, deviceId);
 
         try (final var connection = database.getConnection()) {
             final var session = loadSession(connection, key);
@@ -181,13 +171,13 @@ public class SessionStore implements SignalServiceSessionStore {
 
     @Override
     public void deleteAllSessions(String name) {
-        final var recipientId = resolver.resolveRecipient(name);
-        deleteAllSessions(recipientId);
+        final var serviceId = ServiceId.parseOrThrow(name);
+        deleteAllSessions(serviceId);
     }
 
-    public void deleteAllSessions(RecipientId recipientId) {
+    public void deleteAllSessions(ServiceId serviceId) {
         try (final var connection = database.getConnection()) {
-            deleteAllSessions(connection, recipientId);
+            deleteAllSessions(connection, serviceId);
         } catch (SQLException e) {
             throw new RuntimeException("Failed update session store", e);
         }
@@ -195,6 +185,10 @@ public class SessionStore implements SignalServiceSessionStore {
 
     @Override
     public void archiveSession(final SignalProtocolAddress address) {
+        if (!UuidUtil.isUuid(address.getName())) {
+            return;
+        }
+
         final var key = getKey(address);
 
         try (final var connection = database.getConnection()) {
@@ -212,19 +206,17 @@ public class SessionStore implements SignalServiceSessionStore {
 
     @Override
     public Set<SignalProtocolAddress> getAllAddressesWithActiveSessions(final List<String> addressNames) {
-        final var recipientIdToNameMap = addressNames.stream()
-                .collect(Collectors.toMap(resolver::resolveRecipient, name -> name));
-        final var recipientIdsCommaSeparated = recipientIdToNameMap.keySet()
-                .stream()
-                .map(recipientId -> String.valueOf(recipientId.id()))
+        final var serviceIdsCommaSeparated = addressNames.stream()
+                .map(ServiceId::parseOrThrow)
+                .map(ServiceId::toString)
                 .collect(Collectors.joining(","));
         final var sql = (
                 """
-                SELECT s.recipient_id, s.device_id, s.record
+                SELECT s.uuid, s.device_id, s.record
                 FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id IN (%s)
+                WHERE s.account_id_type = ? AND s.uuid IN (%s)
                 """
-        ).formatted(TABLE_SESSION, recipientIdsCommaSeparated);
+        ).formatted(TABLE_SESSION, serviceIdsCommaSeparated);
         try (final var connection = database.getConnection()) {
             try (final var statement = connection.prepareStatement(sql)) {
                 statement.setInt(1, accountIdType);
@@ -232,8 +224,7 @@ public class SessionStore implements SignalServiceSessionStore {
                                 res -> new Pair<>(getKeyFromResultSet(res), getSessionRecordFromResultSet(res)))
                         .filter(pair -> isActive(pair.second()))
                         .map(Pair::first)
-                        .map(key -> new SignalProtocolAddress(recipientIdToNameMap.get(key.recipientId),
-                                key.deviceId()))
+                        .map(key -> key.serviceId().toProtocolAddress(key.deviceId()))
                         .collect(Collectors.toSet());
             }
         } catch (SQLException e) {
@@ -244,7 +235,7 @@ public class SessionStore implements SignalServiceSessionStore {
     public void archiveAllSessions() {
         final var sql = (
                 """
-                SELECT s.recipient_id, s.device_id, s.record
+                SELECT s.uuid, s.device_id, s.record
                 FROM %s AS s
                 WHERE s.account_id_type = ?
                 """
@@ -267,12 +258,12 @@ public class SessionStore implements SignalServiceSessionStore {
         }
     }
 
-    public void archiveSessions(final RecipientId recipientId) {
+    public void archiveSessions(final ServiceId serviceId) {
         final var sql = (
                 """
-                SELECT s.recipient_id, s.device_id, s.record
+                SELECT s.uuid, s.device_id, s.record
                 FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id = ?
+                WHERE s.account_id_type = ? AND s.uuid = ?
                 """
         ).formatted(TABLE_SESSION);
         try (final var connection = database.getConnection()) {
@@ -280,7 +271,7 @@ public class SessionStore implements SignalServiceSessionStore {
             final List<Pair<Key, SessionRecord>> records;
             try (final var statement = connection.prepareStatement(sql)) {
                 statement.setInt(1, accountIdType);
-                statement.setLong(2, recipientId.id());
+                statement.setBytes(2, serviceId.toByteArray());
                 records = Utils.executeQueryForStream(statement,
                         res -> new Pair<>(getKeyFromResultSet(res), getSessionRecordFromResultSet(res))).toList();
             }
@@ -294,35 +285,6 @@ public class SessionStore implements SignalServiceSessionStore {
         }
     }
 
-    public void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
-        try (final var connection = database.getConnection()) {
-            connection.setAutoCommit(false);
-            synchronized (cachedSessions) {
-                cachedSessions.clear();
-            }
-
-            final var sql = """
-                            UPDATE OR IGNORE %s
-                            SET recipient_id = ?
-                            WHERE account_id_type = ? AND recipient_id = ?
-                            """.formatted(TABLE_SESSION);
-            try (final var statement = connection.prepareStatement(sql)) {
-                statement.setLong(1, recipientId.id());
-                statement.setInt(2, accountIdType);
-                statement.setLong(3, toBeMergedRecipientId.id());
-                final var rows = statement.executeUpdate();
-                if (rows > 0) {
-                    logger.debug("Reassigned {} sessions of to be merged recipient.", rows);
-                }
-            }
-            // Delete all conflicting sessions now
-            deleteAllSessions(connection, toBeMergedRecipientId);
-            connection.commit();
-        } catch (SQLException e) {
-            throw new RuntimeException("Failed update session store", e);
-        }
-    }
-
     void addLegacySessions(final Collection<Pair<Key, SessionRecord>> sessions) {
         logger.debug("Migrating legacy sessions to database");
         long start = System.nanoTime();
@@ -339,8 +301,8 @@ public class SessionStore implements SignalServiceSessionStore {
     }
 
     private Key getKey(final SignalProtocolAddress address) {
-        final var recipientId = resolver.resolveRecipient(address.getName());
-        return new Key(recipientId, address.getDeviceId());
+        final var serviceId = ServiceId.parseOrThrow(address.getName());
+        return new Key(serviceId, address.getDeviceId());
     }
 
     private SessionRecord loadSession(Connection connection, final Key key) throws SQLException {
@@ -354,21 +316,21 @@ public class SessionStore implements SignalServiceSessionStore {
                 """
                 SELECT s.record
                 FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id = ? AND s.device_id = ?
+                WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id = ?
                 """
         ).formatted(TABLE_SESSION);
         try (final var statement = connection.prepareStatement(sql)) {
             statement.setInt(1, accountIdType);
-            statement.setLong(2, key.recipientId().id());
+            statement.setBytes(2, key.serviceId().toByteArray());
             statement.setInt(3, key.deviceId());
             return Utils.executeQueryForOptional(statement, this::getSessionRecordFromResultSet).orElse(null);
         }
     }
 
     private Key getKeyFromResultSet(ResultSet resultSet) throws SQLException {
-        final var recipientId = resultSet.getLong("recipient_id");
+        final var serviceId = ServiceId.parseOrThrow(resultSet.getBytes("uuid"));
         final var deviceId = resultSet.getInt("device_id");
-        return new Key(recipientIdCreator.create(recipientId), deviceId);
+        return new Key(serviceId, deviceId);
     }
 
     private SessionRecord getSessionRecordFromResultSet(ResultSet resultSet) throws SQLException {
@@ -389,19 +351,19 @@ public class SessionStore implements SignalServiceSessionStore {
         }
 
         final var sql = """
-                        INSERT OR REPLACE INTO %s (account_id_type, recipient_id, device_id, record)
+                        INSERT OR REPLACE INTO %s (account_id_type, uuid, device_id, record)
                         VALUES (?, ?, ?, ?)
                         """.formatted(TABLE_SESSION);
         try (final var statement = connection.prepareStatement(sql)) {
             statement.setInt(1, accountIdType);
-            statement.setLong(2, key.recipientId().id());
+            statement.setBytes(2, key.serviceId().toByteArray());
             statement.setInt(3, key.deviceId());
             statement.setBytes(4, session.serialize());
             statement.executeUpdate();
         }
     }
 
-    private void deleteAllSessions(final Connection connection, final RecipientId recipientId) throws SQLException {
+    private void deleteAllSessions(final Connection connection, final ServiceId serviceId) throws SQLException {
         synchronized (cachedSessions) {
             cachedSessions.clear();
         }
@@ -409,12 +371,12 @@ public class SessionStore implements SignalServiceSessionStore {
         final var sql = (
                 """
                 DELETE FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id = ?
+                WHERE s.account_id_type = ? AND s.uuid = ?
                 """
         ).formatted(TABLE_SESSION);
         try (final var statement = connection.prepareStatement(sql)) {
             statement.setInt(1, accountIdType);
-            statement.setLong(2, recipientId.id());
+            statement.setBytes(2, serviceId.toByteArray());
             statement.executeUpdate();
         }
     }
@@ -427,12 +389,12 @@ public class SessionStore implements SignalServiceSessionStore {
         final var sql = (
                 """
                 DELETE FROM %s AS s
-                WHERE s.account_id_type = ? AND s.recipient_id = ? AND s.device_id = ?
+                WHERE s.account_id_type = ? AND s.uuid = ? AND s.device_id = ?
                 """
         ).formatted(TABLE_SESSION);
         try (final var statement = connection.prepareStatement(sql)) {
             statement.setInt(1, accountIdType);
-            statement.setLong(2, key.recipientId().id());
+            statement.setBytes(2, key.serviceId().toByteArray());
             statement.setInt(3, key.deviceId());
             statement.executeUpdate();
         }
@@ -444,5 +406,5 @@ public class SessionStore implements SignalServiceSessionStore {
                 && record.getSessionVersion() == CiphertextMessage.CURRENT_VERSION;
     }
 
-    record Key(RecipientId recipientId, int deviceId) {}
+    record Key(ServiceId serviceId, int deviceId) {}
 }
index 2226c92e62c7c4f3be64d55b0d300d654b43040a..97adb69d1013933561b8315e7acbdab2cf00d3cb 100644 (file)
@@ -1,12 +1,12 @@
 package org.asamk.signal.manager.util;
 
 import org.asamk.signal.manager.api.Pair;
+import org.asamk.signal.manager.storage.recipients.RecipientAddress;
 import org.signal.libsignal.protocol.IdentityKey;
 import org.signal.libsignal.protocol.fingerprint.Fingerprint;
 import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.whispersystems.signalservice.api.push.SignalServiceAddress;
 import org.whispersystems.signalservice.api.util.StreamDetails;
 
 import java.io.BufferedInputStream;
@@ -73,28 +73,27 @@ public class Utils {
 
     public static Fingerprint computeSafetyNumber(
             boolean isUuidCapable,
-            SignalServiceAddress ownAddress,
+            RecipientAddress ownAddress,
             IdentityKey ownIdentityKey,
-            SignalServiceAddress theirAddress,
+            RecipientAddress theirAddress,
             IdentityKey theirIdentityKey
     ) {
         int version;
         byte[] ownId;
         byte[] theirId;
 
-        if (isUuidCapable) {
+        if (!isUuidCapable && ownAddress.number().isPresent() && theirAddress.number().isPresent()) {
+            // Version 1: E164 user
+            version = 1;
+            ownId = ownAddress.number().get().getBytes();
+            theirId = theirAddress.number().get().getBytes();
+        } else if (isUuidCapable && theirAddress.uuid().isPresent()) {
             // Version 2: UUID user
             version = 2;
             ownId = ownAddress.getServiceId().toByteArray();
             theirId = theirAddress.getServiceId().toByteArray();
         } else {
-            // Version 1: E164 user
-            version = 1;
-            if (ownAddress.getNumber().isEmpty() || theirAddress.getNumber().isEmpty()) {
-                return null;
-            }
-            ownId = ownAddress.getNumber().get().getBytes();
-            theirId = theirAddress.getNumber().get().getBytes();
+            return null;
         }
 
         return new NumericFingerprintGenerator(5200).createFor(version,