]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java
Update libsignal-service
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / UnidentifiedAccessHelper.java
index bda24c7cd2c7cfd03dd0be699f0263e318fc5dfc..cd2719a1f12d60f591cc8410a914b9de5fbf7371 100644 (file)
@@ -1,47 +1,93 @@
 package org.asamk.signal.manager.helper;
 
-import org.asamk.signal.manager.SignalDependencies;
 import org.asamk.signal.manager.api.PhoneNumberSharingMode;
+import org.asamk.signal.manager.api.Profile;
+import org.asamk.signal.manager.internal.SignalDependencies;
 import org.asamk.signal.manager.storage.SignalAccount;
-import org.asamk.signal.manager.storage.recipients.Profile;
 import org.asamk.signal.manager.storage.recipients.RecipientId;
+import org.jetbrains.annotations.Nullable;
 import org.signal.libsignal.metadata.certificate.InvalidCertificateException;
 import org.signal.libsignal.metadata.certificate.SenderCertificate;
-import org.signal.zkgroup.profiles.ProfileKey;
+import org.signal.libsignal.zkgroup.profiles.ProfileKey;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.whispersystems.libsignal.util.guava.Optional;
+import org.whispersystems.signalservice.api.crypto.SealedSenderAccess;
 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
-import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
 
 import java.io.IOException;
 import java.util.List;
 import java.util.concurrent.TimeUnit;
 
+import static org.asamk.signal.manager.util.Utils.handleResponseException;
+
 public class UnidentifiedAccessHelper {
 
-    private final static Logger logger = LoggerFactory.getLogger(UnidentifiedAccessHelper.class);
-    private final static long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.DAYS.toMillis(1);
+    private static final Logger logger = LoggerFactory.getLogger(UnidentifiedAccessHelper.class);
+    private static final long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.DAYS.toMillis(1);
     private static final byte[] UNRESTRICTED_KEY = new byte[16];
 
     private final SignalAccount account;
     private final SignalDependencies dependencies;
-    private final SelfProfileKeyProvider selfProfileKeyProvider;
-    private final ProfileProvider profileProvider;
+    private final Context context;
 
     private SenderCertificate privacySenderCertificate;
     private SenderCertificate senderCertificate;
 
-    public UnidentifiedAccessHelper(
-            final SignalAccount account,
-            final SignalDependencies dependencies,
-            final SelfProfileKeyProvider selfProfileKeyProvider,
-            final ProfileProvider profileProvider
-    ) {
-        this.account = account;
-        this.dependencies = dependencies;
-        this.selfProfileKeyProvider = selfProfileKeyProvider;
-        this.profileProvider = profileProvider;
+    public UnidentifiedAccessHelper(final Context context) {
+        this.account = context.getAccount();
+        this.dependencies = context.getDependencies();
+        this.context = context;
+    }
+
+    public void rotateSenderCertificates() {
+        privacySenderCertificate = null;
+        senderCertificate = null;
+    }
+
+    public List<SealedSenderAccess> getSealedSenderAccessFor(List<RecipientId> recipients) {
+        return recipients.stream().map(this::getAccessFor).map(SealedSenderAccess::forIndividual).toList();
+    }
+
+    public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient) {
+        return getSealedSenderAccessFor(recipient, false);
+    }
+
+    public @Nullable SealedSenderAccess getSealedSenderAccessFor(RecipientId recipient, boolean noRefresh) {
+        return SealedSenderAccess.forIndividual(getAccessFor(recipient, noRefresh));
+    }
+
+    public List<UnidentifiedAccess> getAccessFor(List<RecipientId> recipients) {
+        return recipients.stream().map(this::getAccessFor).toList();
+    }
+
+    private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipient) {
+        return getAccessFor(recipient, false);
+    }
+
+    private @Nullable UnidentifiedAccess getAccessFor(RecipientId recipientId, boolean noRefresh) {
+        var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipientId, noRefresh);
+        if (recipientUnidentifiedAccessKey == null) {
+            logger.trace("Unidentified access not available for {}", recipientId);
+            return null;
+        }
+
+        var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh);
+        if (selfUnidentifiedAccessKey == null) {
+            logger.trace("Unidentified access not available for self");
+            return null;
+        }
+
+        var senderCertificate = getSenderCertificateFor(recipientId);
+        if (senderCertificate == null) {
+            logger.trace("Unidentified access not available due to missing sender certificate");
+            return null;
+        }
+
+        try {
+            return new UnidentifiedAccess(recipientUnidentifiedAccessKey, senderCertificate, false);
+        } catch (InvalidCertificateException e) {
+            return null;
+        }
     }
 
     private byte[] getSenderCertificateFor(final RecipientId recipientId) {
@@ -50,10 +96,10 @@ public class UnidentifiedAccessHelper {
                 sharingMode == PhoneNumberSharingMode.CONTACTS
                         && account.getContactStore().getContact(recipientId) != null
         )) {
-            logger.debug("Using normal sender certificate for message to {}", recipientId);
+            logger.trace("Using normal sender certificate for message to {}", recipientId);
             return getSenderCertificate();
         } else {
-            logger.debug("Using phone number privacy sender certificate for message to {}", recipientId);
+            logger.trace("Using phone number privacy sender certificate for message to {}", recipientId);
             return getSenderCertificateForPhoneNumberPrivacy();
         }
     }
@@ -65,11 +111,12 @@ public class UnidentifiedAccessHelper {
             return privacySenderCertificate.getSerialized();
         }
         try {
-            final var certificate = dependencies.getAccountManager().getSenderCertificateForPhoneNumberPrivacy();
+            final var certificate = handleResponseException(dependencies.getCertificateApi()
+                    .getSenderCertificateForPhoneNumberPrivacy());
             privacySenderCertificate = new SenderCertificate(certificate);
             return certificate;
         } catch (IOException | InvalidCertificateException e) {
-            logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
+            logger.warn("Failed to get sender certificate (pnp), ignoring: {}", e.getMessage());
             return null;
         }
     }
@@ -81,7 +128,7 @@ public class UnidentifiedAccessHelper {
             return senderCertificate.getSerialized();
         }
         try {
-            final var certificate = dependencies.getAccountManager().getSenderCertificate();
+            final var certificate = handleResponseException(dependencies.getCertificateApi().getSenderCertificate());
             this.senderCertificate = new SenderCertificate(certificate);
             return certificate;
         } catch (IOException | InvalidCertificateException e) {
@@ -93,18 +140,18 @@ public class UnidentifiedAccessHelper {
     private byte[] getSelfUnidentifiedAccessKey(boolean noRefresh) {
         var selfProfile = noRefresh
                 ? account.getProfileStore().getProfile(account.getSelfRecipientId())
-                : profileProvider.getProfile(account.getSelfRecipientId());
+                : context.getProfileHelper().getSelfProfile();
         if (selfProfile != null
                 && selfProfile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED) {
             return createUnrestrictedUnidentifiedAccess();
         }
-        return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey());
+        return UnidentifiedAccess.deriveAccessKeyFrom(account.getProfileKey());
     }
 
     private byte[] getTargetUnidentifiedAccessKey(RecipientId recipientId, boolean noRefresh) {
         var targetProfile = noRefresh
                 ? account.getProfileStore().getProfile(recipientId)
-                : profileProvider.getProfile(recipientId);
+                : context.getProfileHelper().getRecipientProfile(recipientId);
         if (targetProfile == null) {
             return null;
         }
@@ -114,66 +161,14 @@ public class UnidentifiedAccessHelper {
     }
 
     private static byte[] getTargetUnidentifiedAccessKey(
-            final Profile targetProfile, final ProfileKey theirProfileKey
+            final Profile targetProfile,
+            final ProfileKey theirProfileKey
     ) {
-        switch (targetProfile.getUnidentifiedAccessMode()) {
-            case ENABLED:
-                if (theirProfileKey == null) {
-                    return null;
-                }
-
-                return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
-            case UNRESTRICTED:
-                return createUnrestrictedUnidentifiedAccess();
-            default:
-                return null;
-        }
-    }
-
-    public Optional<UnidentifiedAccessPair> getAccessForSync() {
-        var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(false);
-        var selfUnidentifiedAccessCertificate = getSenderCertificate();
-
-        if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) {
-            return Optional.absent();
-        }
-
-        try {
-            return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
-                    selfUnidentifiedAccessCertificate),
-                    new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
-        } catch (InvalidCertificateException e) {
-            return Optional.absent();
-        }
-    }
-
-    public List<Optional<UnidentifiedAccessPair>> getAccessFor(List<RecipientId> recipients) {
-        return recipients.stream().map(this::getAccessFor).toList();
-    }
-
-    public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipient) {
-        return getAccessFor(recipient, false);
-    }
-
-    public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipient, boolean noRefresh) {
-        var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient, noRefresh);
-        if (recipientUnidentifiedAccessKey == null) {
-            return Optional.absent();
-        }
-
-        var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey(noRefresh);
-        var selfUnidentifiedAccessCertificate = getSenderCertificateFor(recipient);
-        if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) {
-            return Optional.absent();
-        }
-
-        try {
-            return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
-                    selfUnidentifiedAccessCertificate),
-                    new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
-        } catch (InvalidCertificateException e) {
-            return Optional.absent();
-        }
+        return switch (targetProfile.getUnidentifiedAccessMode()) {
+            case ENABLED -> theirProfileKey == null ? null : UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
+            case UNRESTRICTED -> createUnrestrictedUnidentifiedAccess();
+            default -> null;
+        };
     }
 
     private static byte[] createUnrestrictedUnidentifiedAccess() {