From: AsamK Date: Wed, 5 Apr 2023 09:07:53 +0000 (+0200) Subject: Reduce use of unknown serviceIds X-Git-Tag: v0.11.8~3 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/780c69d80476ced44d643d670f3c3e9359eb26e6?hp=22c948166ad8a4a0866dcf5a996c5230702c3d8d Reduce use of unknown serviceIds --- diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index 1dbd9bc6..9696d956 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -676,14 +676,17 @@ class ManagerImpl implements Manager { var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); for (final var recipient : recipients) { - if (recipient instanceof RecipientIdentifier.Single r) { + if (recipient instanceof RecipientIdentifier.Uuid u) { + account.getMessageSendLogStore() + .deleteEntryForRecipientNonGroup(targetSentTimestamp, ServiceId.from(u.uuid())); + } else if (recipient instanceof RecipientIdentifier.Single r) { try { final var recipientId = context.getRecipientHelper().resolveRecipient(r); - account.getMessageSendLogStore() - .deleteEntryForRecipientNonGroup(targetSentTimestamp, - account.getRecipientAddressResolver() - .resolveRecipientAddress(recipientId) - .getServiceId()); + final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); + if (address.serviceId().isPresent()) { + account.getMessageSendLogStore() + .deleteEntryForRecipientNonGroup(targetSentTimestamp, address.serviceId().get()); + } } catch (UnregisteredRecipientException ignored) { } } else if (recipient instanceof RecipientIdentifier.Group r) { @@ -749,8 +752,10 @@ class ManagerImpl implements Manager { final var serviceId = context.getAccount() .getRecipientAddressResolver() .resolveRecipientAddress(recipientId) - .getServiceId(); - account.getAciSessionStore().deleteAllSessions(serviceId); + .serviceId(); + if (serviceId.isPresent()) { + account.getAciSessionStore().deleteAllSessions(serviceId.get()); + } } } } @@ -1131,9 +1136,12 @@ class ManagerImpl implements Manager { public List getIdentities(RecipientIdentifier.Single recipient) { ServiceId serviceId; try { - serviceId = account.getRecipientAddressResolver() - .resolveRecipientAddress(context.getRecipientHelper().resolveRecipient(recipient)) - .getServiceId(); + final var address = account.getRecipientAddressResolver() + .resolveRecipientAddress(context.getRecipientHelper().resolveRecipient(recipient)); + if (address.serviceId().isEmpty()) { + return List.of(); + } + serviceId = address.serviceId().get(); } catch (UnregisteredRecipientException e) { return List.of(); } diff --git a/lib/src/main/java/org/asamk/signal/manager/api/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/api/RecipientAddress.java index ee3bded9..f500e3a9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/api/RecipientAddress.java +++ b/lib/src/main/java/org/asamk/signal/manager/api/RecipientAddress.java @@ -39,10 +39,6 @@ public record RecipientAddress(Optional uuid, Optional number, Opt this(Optional.of(uuid), Optional.empty(), Optional.empty()); } - public ServiceId getServiceId() { - return ServiceId.from(uuid.orElse(UNKNOWN_UUID)); - } - public String getIdentifier() { if (uuid.isPresent()) { return uuid.get().toString(); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java index b908823a..df762c91 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java @@ -2,7 +2,6 @@ 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; @@ -17,7 +16,7 @@ import org.whispersystems.signalservice.api.push.ServiceId; import java.io.IOException; import java.util.Arrays; -import java.util.function.Function; +import java.util.function.BiFunction; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -34,22 +33,19 @@ public class IdentityHelper { } public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) { - final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); - return trustIdentity(serviceId, - identityKey -> Arrays.equals(identityKey.serialize(), fingerprint), + return trustIdentity(recipientId, + (serviceId, identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint), TrustLevel.TRUSTED_VERIFIED); } public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) { - final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); - return trustIdentity(serviceId, - identityKey -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)), + return trustIdentity(recipientId, + (serviceId, identityKey) -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)), TrustLevel.TRUSTED_VERIFIED); } public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) { - final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); - return trustIdentity(serviceId, identityKey -> { + return trustIdentity(recipientId, (serviceId, identityKey) -> { final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey); try { return fingerprint != null && fingerprint.compareTo(safetyNumber); @@ -60,8 +56,7 @@ public class IdentityHelper { } public boolean trustIdentityAllKeys(RecipientId recipientId) { - final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); - return trustIdentity(serviceId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); + return trustIdentity(recipientId, (serviceId, identityKey) -> true, TrustLevel.TRUSTED_UNVERIFIED); } public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) { @@ -77,35 +72,50 @@ public class IdentityHelper { private Fingerprint computeSafetyNumberFingerprint( final ServiceId serviceId, final IdentityKey theirIdentityKey ) { - final var address = account.getRecipientAddressResolver() - .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId)); + final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId); + final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId); - return Utils.computeSafetyNumber(capabilities.getUuid(), - account.getSelfRecipientAddress(), + if (capabilities.getUuid()) { + if (serviceId.isUnknown()) { + return null; + } + return Utils.computeSafetyNumberForUuid(account.getAci(), + account.getAciIdentityKeyPair().getPublicKey(), + serviceId, + theirIdentityKey); + } + if (address.number().isEmpty()) { + return null; + } + return Utils.computeSafetyNumberForNumber(account.getNumber(), account.getAciIdentityKeyPair().getPublicKey(), - address.getServiceId().equals(serviceId) - ? address - : new RecipientAddress(serviceId, address.number().orElse(null)), + address.number().get(), theirIdentityKey); } private boolean trustIdentity( - ServiceId serviceId, Function verifier, TrustLevel trustLevel + RecipientId recipientId, BiFunction verifier, TrustLevel trustLevel ) { + final var serviceId = account.getRecipientAddressResolver() + .resolveRecipientAddress(recipientId) + .serviceId() + .orElse(null); + if (serviceId == null) { + return false; + } var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId); if (identity == null) { return false; } - if (!verifier.apply(identity.getIdentityKey())) { + if (!verifier.apply(serviceId, identity.getIdentityKey())) { return false; } account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel); try { - final var address = account.getRecipientAddressResolver() - .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId)) - .toSignalServiceAddress(); + final var address = context.getRecipientHelper() + .resolveSignalServiceAddress(account.getRecipientResolver().resolveRecipient(serviceId)); context.getSyncHelper().sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel); } catch (IOException e) { logger.warn("Failed to send verification sync message: {}", e.getMessage()); diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java index 2c5dbe69..c20fc692 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/ReceiveHelper.java @@ -12,6 +12,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.SignalWebSocket; import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; +import org.whispersystems.signalservice.api.push.ServiceId; import org.whispersystems.signalservice.api.websocket.WebSocketConnectionState; import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; @@ -225,8 +226,9 @@ public class ReceiveHelper { if (exception instanceof UntrustedIdentityException) { logger.debug("Keeping message with untrusted identity in message cache"); final var address = ((UntrustedIdentityException) exception).getSender(); - final var recipientId = account.getRecipientResolver().resolveRecipient(address.getServiceId()); - if (!envelope.hasSourceUuid()) { + if (!envelope.hasSourceUuid() && address.uuid().isPresent()) { + final var recipientId = account.getRecipientResolver() + .resolveRecipient(ServiceId.from(address.uuid().get())); try { cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); } catch (IOException ioException) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java index 9243ceba..2a3866a5 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/RecipientHelper.java @@ -182,8 +182,8 @@ public class RecipientHelper { public record RegisteredUser(Optional aci, Optional pni) { public RegisteredUser { - aci = aci.isPresent() && aci.get().equals(ServiceId.UNKNOWN) ? Optional.empty() : aci; - pni = pni.isPresent() && pni.get().equals(ServiceId.UNKNOWN) ? Optional.empty() : pni; + aci = aci.isPresent() && aci.get().isUnknown() ? Optional.empty() : aci; + pni = pni.isPresent() && pni.get().isUnknown() ? Optional.empty() : pni; if (aci.isEmpty() && pni.isEmpty()) { throw new AssertionError("Must have either a ACI or PNI!"); } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java index d14c3807..0f4d035d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/SendHelper.java @@ -492,7 +492,11 @@ public class SendHelper { final var serviceId = account.getRecipientAddressResolver() .resolveRecipientAddress(recipientId) - .getServiceId(); + .serviceId() + .orElse(null); + if (serviceId == null) { + continue; + } final var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId); if (identity == null || !identity.getTrustLevel().isTrusted()) { continue; diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java index 68eab7f0..50eceb67 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/StorageHelper.java @@ -101,11 +101,15 @@ public class StorageHelper { } final var contactRecord = record.getContact().get(); - final var address = new RecipientAddress(contactRecord.getServiceId(), contactRecord.getNumber().orElse(null)); + final var serviceId = contactRecord.getServiceId(); + if (contactRecord.getNumber().isEmpty() && serviceId.isUnknown()) { + return; + } + final var address = new RecipientAddress(serviceId, contactRecord.getNumber().orElse(null)); var recipientId = account.getRecipientResolver().resolveRecipient(address); - if (contactRecord.getUsername().isPresent()) { + if (serviceId.isValid() && contactRecord.getUsername().isPresent()) { recipientId = account.getRecipientTrustedResolver() - .resolveRecipientTrusted(contactRecord.getServiceId(), contactRecord.getUsername().get()); + .resolveRecipientTrusted(serviceId, contactRecord.getUsername().get()); } final var contact = account.getContactStore().getContact(recipientId); @@ -166,16 +170,15 @@ public class StorageHelper { logger.warn("Received invalid contact profile key from storage"); } } - if (contactRecord.getIdentityKey().isPresent()) { + if (contactRecord.getIdentityKey().isPresent() && serviceId.isValid()) { try { logger.trace("Storing identity key {}", recipientId); final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); - account.getIdentityKeyStore().saveIdentity(address.getServiceId(), identityKey); + account.getIdentityKeyStore().saveIdentity(serviceId, identityKey); final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState()); if (trustLevel != null) { - account.getIdentityKeyStore() - .setIdentityTrustLevel(address.getServiceId(), identityKey, trustLevel); + account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identityKey, trustLevel); } } catch (InvalidKeyException e) { logger.warn("Received invalid contact identity key from storage"); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java index b104eb84..bd892c5d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java @@ -813,7 +813,7 @@ public class SignalAccount implements Closeable { if (identity.getAddress().serviceId().isEmpty()) { continue; } - final var serviceId = identity.getAddress().getServiceId(); + final var serviceId = identity.getAddress().serviceId().get(); getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey()); getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java b/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java index aa800a04..a2f5162e 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/identities/LegacyIdentityKeyStore.java @@ -72,6 +72,10 @@ public class LegacyIdentityKeyStore { if (!file.exists()) { return null; } + final var address = addressResolver.resolveRecipientAddress(recipientId); + if (address.serviceId().isEmpty()) { + return null; + } try (var inputStream = new FileInputStream(file)) { var storage = objectMapper.readValue(inputStream, IdentityStorage.class); @@ -79,7 +83,7 @@ public class LegacyIdentityKeyStore { var trustLevel = TrustLevel.fromInt(storage.trustLevel()); var added = storage.addedTimestamp(); - final var serviceId = addressResolver.resolveRecipientAddress(recipientId).getServiceId(); + final var serviceId = address.serviceId().get(); return new IdentityInfo(serviceId, id, trustLevel, added); } catch (IOException | InvalidKeyException e) { logger.warn("Failed to load identity key: {}", e.getMessage()); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java index d94a93a3..f323a5cb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/recipients/RecipientAddress.java @@ -17,10 +17,10 @@ public record RecipientAddress( * @param number The phone number of the user, if available. */ public RecipientAddress { - if (serviceId.isPresent() && serviceId.get().equals(ServiceId.UNKNOWN)) { + if (serviceId.isPresent() && serviceId.get().isUnknown()) { serviceId = Optional.empty(); } - if (pni.isPresent() && pni.get().equals(ServiceId.UNKNOWN)) { + if (pni.isPresent() && pni.get().isUnknown()) { pni = Optional.empty(); } if (serviceId.isEmpty() && pni.isPresent()) { @@ -88,10 +88,6 @@ public record RecipientAddress( address.username.equals(this.username) ? Optional.empty() : this.username); } - public ServiceId getServiceId() { - return serviceId.orElse(ServiceId.UNKNOWN); - } - public String getIdentifier() { if (serviceId.isPresent()) { return serviceId.get().toString(); @@ -173,7 +169,7 @@ public record RecipientAddress( } public SignalServiceAddress toSignalServiceAddress() { - return new SignalServiceAddress(getServiceId(), number); + return new SignalServiceAddress(serviceId.orElse(ServiceId.UNKNOWN), number); } public org.asamk.signal.manager.api.RecipientAddress toApiRecipientAddress() { diff --git a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java index efa72bf2..16e80e45 100644 --- a/lib/src/main/java/org/asamk/signal/manager/util/Utils.java +++ b/lib/src/main/java/org/asamk/signal/manager/util/Utils.java @@ -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.ServiceId; import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.internal.ServiceResponse; @@ -56,31 +56,35 @@ public class Utils { } } - public static Fingerprint computeSafetyNumber( - boolean isUuidCapable, - RecipientAddress ownAddress, - IdentityKey ownIdentityKey, - RecipientAddress theirAddress, - IdentityKey theirIdentityKey + public static Fingerprint computeSafetyNumberForNumber( + String ownNumber, IdentityKey ownIdentityKey, String theirNumber, IdentityKey theirIdentityKey ) { - int version; - byte[] ownId; - byte[] theirId; - - 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.serviceId().isPresent()) { - // Version 2: UUID user - version = 2; - ownId = ownAddress.getServiceId().toByteArray(); - theirId = theirAddress.getServiceId().toByteArray(); - } else { - return null; - } + // Version 1: E164 user + final var version = 1; + final var ownId = ownNumber.getBytes(StandardCharsets.UTF_8); + final var theirId = theirNumber.getBytes(StandardCharsets.UTF_8); + + return getFingerprint(version, ownId, ownIdentityKey, theirId, theirIdentityKey); + } + + public static Fingerprint computeSafetyNumberForUuid( + ServiceId ownServiceId, IdentityKey ownIdentityKey, ServiceId theirServiceId, IdentityKey theirIdentityKey + ) { + // Version 2: UUID user + final var version = 2; + final var ownId = ownServiceId.toByteArray(); + final var theirId = theirServiceId.toByteArray(); + + return getFingerprint(version, ownId, ownIdentityKey, theirId, theirIdentityKey); + } + private static Fingerprint getFingerprint( + final int version, + final byte[] ownId, + final IdentityKey ownIdentityKey, + final byte[] theirId, + final IdentityKey theirIdentityKey + ) { return new NumericFingerprintGenerator(5200).createFor(version, ownId, ownIdentityKey,