mirror of
https://github.com/AsamK/signal-cli
synced 2025-08-29 02:20:39 +00:00
Reduce use of unknown serviceIds
This commit is contained in:
parent
22c948166a
commit
780c69d804
11 changed files with 110 additions and 83 deletions
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue