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) {
final var serviceId = context.getAccount()
.getRecipientAddressResolver()
.resolveRecipientAddress(recipientId)
- .getServiceId();
- account.getAciSessionStore().deleteAllSessions(serviceId);
+ .serviceId();
+ if (serviceId.isPresent()) {
+ account.getAciSessionStore().deleteAllSessions(serviceId.get());
+ }
}
}
}
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();
}
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();
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;
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;
}
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);
}
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) {
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());
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;
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) {
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!");
}
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;
}
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);
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");
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(),
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);
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());
* @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()) {
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();
}
public SignalServiceAddress toSignalServiceAddress() {
- return new SignalServiceAddress(getServiceId(), number);
+ return new SignalServiceAddress(serviceId.orElse(ServiceId.UNKNOWN), number);
}
public org.asamk.signal.manager.api.RecipientAddress toApiRecipientAddress() {
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;
}
}
- 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,