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);
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<Identity> 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();
}

View file

@ -39,10 +39,6 @@ public record RecipientAddress(Optional<UUID> uuid, Optional<String> 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();

View file

@ -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<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);
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());

View file

@ -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) {

View file

@ -182,8 +182,8 @@ public class RecipientHelper {
public record RegisteredUser(Optional<ACI> aci, Optional<PNI> 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!");
}

View file

@ -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;

View file

@ -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");

View file

@ -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(),

View file

@ -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());

View file

@ -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() {

View 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.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;
// Version 1: E164 user
final var version = 1;
final var ownId = ownNumber.getBytes(StandardCharsets.UTF_8);
final var theirId = theirNumber.getBytes(StandardCharsets.UTF_8);
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;
}
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,