Reduce use of unknown serviceIds

This commit is contained in:
AsamK 2023-04-05 11:07:53 +02:00
parent 22c948166a
commit 780c69d804
11 changed files with 110 additions and 83 deletions

View file

@ -676,14 +676,17 @@ class ManagerImpl implements Manager {
var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp); var delete = new SignalServiceDataMessage.RemoteDelete(targetSentTimestamp);
final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete); final var messageBuilder = SignalServiceDataMessage.newBuilder().withRemoteDelete(delete);
for (final var recipient : recipients) { 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 { try {
final var recipientId = context.getRecipientHelper().resolveRecipient(r); final var recipientId = context.getRecipientHelper().resolveRecipient(r);
final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
if (address.serviceId().isPresent()) {
account.getMessageSendLogStore() account.getMessageSendLogStore()
.deleteEntryForRecipientNonGroup(targetSentTimestamp, .deleteEntryForRecipientNonGroup(targetSentTimestamp, address.serviceId().get());
account.getRecipientAddressResolver() }
.resolveRecipientAddress(recipientId)
.getServiceId());
} catch (UnregisteredRecipientException ignored) { } catch (UnregisteredRecipientException ignored) {
} }
} else if (recipient instanceof RecipientIdentifier.Group r) { } else if (recipient instanceof RecipientIdentifier.Group r) {
@ -749,8 +752,10 @@ class ManagerImpl implements Manager {
final var serviceId = context.getAccount() final var serviceId = context.getAccount()
.getRecipientAddressResolver() .getRecipientAddressResolver()
.resolveRecipientAddress(recipientId) .resolveRecipientAddress(recipientId)
.getServiceId(); .serviceId();
account.getAciSessionStore().deleteAllSessions(serviceId); if (serviceId.isPresent()) {
account.getAciSessionStore().deleteAllSessions(serviceId.get());
}
} }
} }
} }
@ -1131,9 +1136,12 @@ class ManagerImpl implements Manager {
public List<Identity> getIdentities(RecipientIdentifier.Single recipient) { public List<Identity> getIdentities(RecipientIdentifier.Single recipient) {
ServiceId serviceId; ServiceId serviceId;
try { try {
serviceId = account.getRecipientAddressResolver() final var address = account.getRecipientAddressResolver()
.resolveRecipientAddress(context.getRecipientHelper().resolveRecipient(recipient)) .resolveRecipientAddress(context.getRecipientHelper().resolveRecipient(recipient));
.getServiceId(); if (address.serviceId().isEmpty()) {
return List.of();
}
serviceId = address.serviceId().get();
} catch (UnregisteredRecipientException e) { } catch (UnregisteredRecipientException e) {
return List.of(); return List.of();
} }

View file

@ -39,10 +39,6 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> number, Opt
this(Optional.of(uuid), Optional.empty(), Optional.empty()); this(Optional.of(uuid), Optional.empty(), Optional.empty());
} }
public ServiceId getServiceId() {
return ServiceId.from(uuid.orElse(UNKNOWN_UUID));
}
public String getIdentifier() { public String getIdentifier() {
if (uuid.isPresent()) { if (uuid.isPresent()) {
return uuid.get().toString(); return uuid.get().toString();

View file

@ -2,7 +2,6 @@ package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.api.TrustLevel; import org.asamk.signal.manager.api.TrustLevel;
import org.asamk.signal.manager.storage.SignalAccount; 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.storage.recipients.RecipientId;
import org.asamk.signal.manager.util.Utils; import org.asamk.signal.manager.util.Utils;
import org.signal.libsignal.protocol.IdentityKey; import org.signal.libsignal.protocol.IdentityKey;
@ -17,7 +16,7 @@ import org.whispersystems.signalservice.api.push.ServiceId;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.Arrays;
import java.util.function.Function; import java.util.function.BiFunction;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities; import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
@ -34,22 +33,19 @@ public class IdentityHelper {
} }
public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) { public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) {
final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); return trustIdentity(recipientId,
return trustIdentity(serviceId, (serviceId, identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint),
identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
TrustLevel.TRUSTED_VERIFIED); TrustLevel.TRUSTED_VERIFIED);
} }
public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) { public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); return trustIdentity(recipientId,
return trustIdentity(serviceId, (serviceId, identityKey) -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)),
identityKey -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)),
TrustLevel.TRUSTED_VERIFIED); TrustLevel.TRUSTED_VERIFIED);
} }
public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) { public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); return trustIdentity(recipientId, (serviceId, identityKey) -> {
return trustIdentity(serviceId, identityKey -> {
final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey); final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey);
try { try {
return fingerprint != null && fingerprint.compareTo(safetyNumber); return fingerprint != null && fingerprint.compareTo(safetyNumber);
@ -60,8 +56,7 @@ public class IdentityHelper {
} }
public boolean trustIdentityAllKeys(RecipientId recipientId) { public boolean trustIdentityAllKeys(RecipientId recipientId) {
final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId(); return trustIdentity(recipientId, (serviceId, identityKey) -> true, TrustLevel.TRUSTED_UNVERIFIED);
return trustIdentity(serviceId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
} }
public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) { public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) {
@ -77,35 +72,50 @@ public class IdentityHelper {
private Fingerprint computeSafetyNumberFingerprint( private Fingerprint computeSafetyNumberFingerprint(
final ServiceId serviceId, final IdentityKey theirIdentityKey final ServiceId serviceId, final IdentityKey theirIdentityKey
) { ) {
final var address = account.getRecipientAddressResolver() final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
.resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId)); final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
return Utils.computeSafetyNumber(capabilities.getUuid(), if (capabilities.getUuid()) {
account.getSelfRecipientAddress(), if (serviceId.isUnknown()) {
return null;
}
return Utils.computeSafetyNumberForUuid(account.getAci(),
account.getAciIdentityKeyPair().getPublicKey(), account.getAciIdentityKeyPair().getPublicKey(),
address.getServiceId().equals(serviceId) serviceId,
? address theirIdentityKey);
: new RecipientAddress(serviceId, address.number().orElse(null)), }
if (address.number().isEmpty()) {
return null;
}
return Utils.computeSafetyNumberForNumber(account.getNumber(),
account.getAciIdentityKeyPair().getPublicKey(),
address.number().get(),
theirIdentityKey); theirIdentityKey);
} }
private boolean trustIdentity( private boolean trustIdentity(
ServiceId serviceId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel RecipientId recipientId, BiFunction<ServiceId, IdentityKey, Boolean> verifier, TrustLevel trustLevel
) { ) {
final var serviceId = account.getRecipientAddressResolver()
.resolveRecipientAddress(recipientId)
.serviceId()
.orElse(null);
if (serviceId == null) {
return false;
}
var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId); var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
if (identity == null) { if (identity == null) {
return false; return false;
} }
if (!verifier.apply(identity.getIdentityKey())) { if (!verifier.apply(serviceId, identity.getIdentityKey())) {
return false; return false;
} }
account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel); account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel);
try { try {
final var address = account.getRecipientAddressResolver() final var address = context.getRecipientHelper()
.resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId)) .resolveSignalServiceAddress(account.getRecipientResolver().resolveRecipient(serviceId));
.toSignalServiceAddress();
context.getSyncHelper().sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel); context.getSyncHelper().sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
} catch (IOException e) { } catch (IOException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage()); logger.warn("Failed to send verification sync message: {}", e.getMessage());

View file

@ -12,6 +12,7 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.SignalWebSocket; import org.whispersystems.signalservice.api.SignalWebSocket;
import org.whispersystems.signalservice.api.messages.SignalServiceEnvelope; 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.WebSocketConnectionState;
import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException; import org.whispersystems.signalservice.api.websocket.WebSocketUnavailableException;
@ -225,8 +226,9 @@ public class ReceiveHelper {
if (exception instanceof UntrustedIdentityException) { if (exception instanceof UntrustedIdentityException) {
logger.debug("Keeping message with untrusted identity in message cache"); logger.debug("Keeping message with untrusted identity in message cache");
final var address = ((UntrustedIdentityException) exception).getSender(); final var address = ((UntrustedIdentityException) exception).getSender();
final var recipientId = account.getRecipientResolver().resolveRecipient(address.getServiceId()); if (!envelope.hasSourceUuid() && address.uuid().isPresent()) {
if (!envelope.hasSourceUuid()) { final var recipientId = account.getRecipientResolver()
.resolveRecipient(ServiceId.from(address.uuid().get()));
try { try {
cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId); cachedMessage[0] = account.getMessageCache().replaceSender(cachedMessage[0], recipientId);
} catch (IOException ioException) { } catch (IOException ioException) {

View file

@ -182,8 +182,8 @@ public class RecipientHelper {
public record RegisteredUser(Optional<ACI> aci, Optional<PNI> pni) { public record RegisteredUser(Optional<ACI> aci, Optional<PNI> pni) {
public RegisteredUser { public RegisteredUser {
aci = aci.isPresent() && aci.get().equals(ServiceId.UNKNOWN) ? Optional.empty() : aci; aci = aci.isPresent() && aci.get().isUnknown() ? Optional.empty() : aci;
pni = pni.isPresent() && pni.get().equals(ServiceId.UNKNOWN) ? Optional.empty() : pni; pni = pni.isPresent() && pni.get().isUnknown() ? Optional.empty() : pni;
if (aci.isEmpty() && pni.isEmpty()) { if (aci.isEmpty() && pni.isEmpty()) {
throw new AssertionError("Must have either a ACI or PNI!"); throw new AssertionError("Must have either a ACI or PNI!");
} }

View file

@ -492,7 +492,11 @@ public class SendHelper {
final var serviceId = account.getRecipientAddressResolver() final var serviceId = account.getRecipientAddressResolver()
.resolveRecipientAddress(recipientId) .resolveRecipientAddress(recipientId)
.getServiceId(); .serviceId()
.orElse(null);
if (serviceId == null) {
continue;
}
final var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId); final var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
if (identity == null || !identity.getTrustLevel().isTrusted()) { if (identity == null || !identity.getTrustLevel().isTrusted()) {
continue; continue;

View file

@ -101,11 +101,15 @@ public class StorageHelper {
} }
final var contactRecord = record.getContact().get(); 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); var recipientId = account.getRecipientResolver().resolveRecipient(address);
if (contactRecord.getUsername().isPresent()) { if (serviceId.isValid() && contactRecord.getUsername().isPresent()) {
recipientId = account.getRecipientTrustedResolver() recipientId = account.getRecipientTrustedResolver()
.resolveRecipientTrusted(contactRecord.getServiceId(), contactRecord.getUsername().get()); .resolveRecipientTrusted(serviceId, contactRecord.getUsername().get());
} }
final var contact = account.getContactStore().getContact(recipientId); final var contact = account.getContactStore().getContact(recipientId);
@ -166,16 +170,15 @@ public class StorageHelper {
logger.warn("Received invalid contact profile key from storage"); logger.warn("Received invalid contact profile key from storage");
} }
} }
if (contactRecord.getIdentityKey().isPresent()) { if (contactRecord.getIdentityKey().isPresent() && serviceId.isValid()) {
try { try {
logger.trace("Storing identity key {}", recipientId); logger.trace("Storing identity key {}", recipientId);
final var identityKey = new IdentityKey(contactRecord.getIdentityKey().get()); 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()); final var trustLevel = TrustLevel.fromIdentityState(contactRecord.getIdentityState());
if (trustLevel != null) { if (trustLevel != null) {
account.getIdentityKeyStore() account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identityKey, trustLevel);
.setIdentityTrustLevel(address.getServiceId(), identityKey, trustLevel);
} }
} catch (InvalidKeyException e) { } catch (InvalidKeyException e) {
logger.warn("Received invalid contact identity key from storage"); logger.warn("Received invalid contact identity key from storage");

View file

@ -813,7 +813,7 @@ public class SignalAccount implements Closeable {
if (identity.getAddress().serviceId().isEmpty()) { if (identity.getAddress().serviceId().isEmpty()) {
continue; continue;
} }
final var serviceId = identity.getAddress().getServiceId(); final var serviceId = identity.getAddress().serviceId().get();
getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey()); getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey());
getIdentityKeyStore().setIdentityTrustLevel(serviceId, getIdentityKeyStore().setIdentityTrustLevel(serviceId,
identity.getIdentityKey(), identity.getIdentityKey(),

View file

@ -72,6 +72,10 @@ public class LegacyIdentityKeyStore {
if (!file.exists()) { if (!file.exists()) {
return null; return null;
} }
final var address = addressResolver.resolveRecipientAddress(recipientId);
if (address.serviceId().isEmpty()) {
return null;
}
try (var inputStream = new FileInputStream(file)) { try (var inputStream = new FileInputStream(file)) {
var storage = objectMapper.readValue(inputStream, IdentityStorage.class); var storage = objectMapper.readValue(inputStream, IdentityStorage.class);
@ -79,7 +83,7 @@ public class LegacyIdentityKeyStore {
var trustLevel = TrustLevel.fromInt(storage.trustLevel()); var trustLevel = TrustLevel.fromInt(storage.trustLevel());
var added = storage.addedTimestamp(); var added = storage.addedTimestamp();
final var serviceId = addressResolver.resolveRecipientAddress(recipientId).getServiceId(); final var serviceId = address.serviceId().get();
return new IdentityInfo(serviceId, id, trustLevel, added); return new IdentityInfo(serviceId, id, trustLevel, added);
} catch (IOException | InvalidKeyException e) { } catch (IOException | InvalidKeyException e) {
logger.warn("Failed to load identity key: {}", e.getMessage()); logger.warn("Failed to load identity key: {}", e.getMessage());

View file

@ -17,10 +17,10 @@ public record RecipientAddress(
* @param number The phone number of the user, if available. * @param number The phone number of the user, if available.
*/ */
public RecipientAddress { public RecipientAddress {
if (serviceId.isPresent() && serviceId.get().equals(ServiceId.UNKNOWN)) { if (serviceId.isPresent() && serviceId.get().isUnknown()) {
serviceId = Optional.empty(); serviceId = Optional.empty();
} }
if (pni.isPresent() && pni.get().equals(ServiceId.UNKNOWN)) { if (pni.isPresent() && pni.get().isUnknown()) {
pni = Optional.empty(); pni = Optional.empty();
} }
if (serviceId.isEmpty() && pni.isPresent()) { if (serviceId.isEmpty() && pni.isPresent()) {
@ -88,10 +88,6 @@ public record RecipientAddress(
address.username.equals(this.username) ? Optional.empty() : this.username); address.username.equals(this.username) ? Optional.empty() : this.username);
} }
public ServiceId getServiceId() {
return serviceId.orElse(ServiceId.UNKNOWN);
}
public String getIdentifier() { public String getIdentifier() {
if (serviceId.isPresent()) { if (serviceId.isPresent()) {
return serviceId.get().toString(); return serviceId.get().toString();
@ -173,7 +169,7 @@ public record RecipientAddress(
} }
public SignalServiceAddress toSignalServiceAddress() { public SignalServiceAddress toSignalServiceAddress() {
return new SignalServiceAddress(getServiceId(), number); return new SignalServiceAddress(serviceId.orElse(ServiceId.UNKNOWN), number);
} }
public org.asamk.signal.manager.api.RecipientAddress toApiRecipientAddress() { public org.asamk.signal.manager.api.RecipientAddress toApiRecipientAddress() {

View file

@ -1,12 +1,12 @@
package org.asamk.signal.manager.util; package org.asamk.signal.manager.util;
import org.asamk.signal.manager.api.Pair; 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.IdentityKey;
import org.signal.libsignal.protocol.fingerprint.Fingerprint; import org.signal.libsignal.protocol.fingerprint.Fingerprint;
import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator; import org.signal.libsignal.protocol.fingerprint.NumericFingerprintGenerator;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.ServiceId;
import org.whispersystems.signalservice.api.util.StreamDetails; import org.whispersystems.signalservice.api.util.StreamDetails;
import org.whispersystems.signalservice.internal.ServiceResponse; import org.whispersystems.signalservice.internal.ServiceResponse;
@ -56,31 +56,35 @@ public class Utils {
} }
} }
public static Fingerprint computeSafetyNumber( public static Fingerprint computeSafetyNumberForNumber(
boolean isUuidCapable, String ownNumber, IdentityKey ownIdentityKey, String theirNumber, IdentityKey theirIdentityKey
RecipientAddress ownAddress,
IdentityKey ownIdentityKey,
RecipientAddress theirAddress,
IdentityKey theirIdentityKey
) { ) {
int version;
byte[] ownId;
byte[] theirId;
if (!isUuidCapable && ownAddress.number().isPresent() && theirAddress.number().isPresent()) {
// Version 1: E164 user // Version 1: E164 user
version = 1; final var version = 1;
ownId = ownAddress.number().get().getBytes(); final var ownId = ownNumber.getBytes(StandardCharsets.UTF_8);
theirId = theirAddress.number().get().getBytes(); final var theirId = theirNumber.getBytes(StandardCharsets.UTF_8);
} else if (isUuidCapable && theirAddress.serviceId().isPresent()) {
// Version 2: UUID user return getFingerprint(version, ownId, ownIdentityKey, theirId, theirIdentityKey);
version = 2;
ownId = ownAddress.getServiceId().toByteArray();
theirId = theirAddress.getServiceId().toByteArray();
} else {
return null;
} }
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, return new NumericFingerprintGenerator(5200).createFor(version,
ownId, ownId,
ownIdentityKey, ownIdentityKey,