Extract IdentityHelper

This commit is contained in:
AsamK 2021-10-12 22:14:39 +02:00
parent 997b3c6a2a
commit f094cd6806
5 changed files with 151 additions and 98 deletions

View file

@ -22,7 +22,6 @@ import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.storage.recipients.Contact; import org.asamk.signal.manager.storage.recipients.Contact;
import org.asamk.signal.manager.storage.recipients.Profile; import org.asamk.signal.manager.storage.recipients.Profile;
import org.asamk.signal.manager.storage.recipients.RecipientAddress; import org.asamk.signal.manager.storage.recipients.RecipientAddress;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -228,8 +227,6 @@ public interface Manager extends Closeable {
boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient); boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient);
String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey);
SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address); SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address);
@Override @Override

View file

@ -38,6 +38,7 @@ import org.asamk.signal.manager.helper.AttachmentHelper;
import org.asamk.signal.manager.helper.ContactHelper; import org.asamk.signal.manager.helper.ContactHelper;
import org.asamk.signal.manager.helper.GroupHelper; import org.asamk.signal.manager.helper.GroupHelper;
import org.asamk.signal.manager.helper.GroupV2Helper; import org.asamk.signal.manager.helper.GroupV2Helper;
import org.asamk.signal.manager.helper.IdentityHelper;
import org.asamk.signal.manager.helper.IncomingMessageHandler; import org.asamk.signal.manager.helper.IncomingMessageHandler;
import org.asamk.signal.manager.helper.PinHelper; import org.asamk.signal.manager.helper.PinHelper;
import org.asamk.signal.manager.helper.PreKeyHelper; import org.asamk.signal.manager.helper.PreKeyHelper;
@ -59,15 +60,10 @@ import org.asamk.signal.manager.storage.stickers.Sticker;
import org.asamk.signal.manager.storage.stickers.StickerPackId; import org.asamk.signal.manager.storage.stickers.StickerPackId;
import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.KeyUtils;
import org.asamk.signal.manager.util.StickerUtils; import org.asamk.signal.manager.util.StickerUtils;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.ecc.ECPublicKey; import org.whispersystems.libsignal.ecc.ECPublicKey;
import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
import org.whispersystems.signalservice.api.SignalSessionLock; import org.whispersystems.signalservice.api.SignalSessionLock;
@ -98,9 +94,7 @@ import java.net.URISyntaxException;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.SignatureException; import java.security.SignatureException;
import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.Date;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -112,7 +106,6 @@ import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; import java.util.concurrent.TimeoutException;
import java.util.concurrent.locks.ReentrantLock; import java.util.concurrent.locks.ReentrantLock;
import java.util.function.Function;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities; import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
@ -138,6 +131,7 @@ public class ManagerImpl implements Manager {
private final ContactHelper contactHelper; private final ContactHelper contactHelper;
private final IncomingMessageHandler incomingMessageHandler; private final IncomingMessageHandler incomingMessageHandler;
private final PreKeyHelper preKeyHelper; private final PreKeyHelper preKeyHelper;
private final IdentityHelper identityHelper;
private final Context context; private final Context context;
private boolean hasCaughtUpWithOldMessages = false; private boolean hasCaughtUpWithOldMessages = false;
@ -238,6 +232,11 @@ public class ManagerImpl implements Manager {
syncHelper, syncHelper,
this::getRecipientProfile, this::getRecipientProfile,
jobExecutor); jobExecutor);
this.identityHelper = new IdentityHelper(account,
dependencies,
this::resolveSignalServiceAddress,
syncHelper,
profileHelper);
} }
@Override @Override
@ -1042,7 +1041,7 @@ public class ManagerImpl implements Manager {
return toGroup(groupHelper.getGroup(groupId)); return toGroup(groupHelper.getGroup(groupId));
} }
public GroupInfo getGroupInfo(GroupId groupId) { private GroupInfo getGroupInfo(GroupId groupId) {
return groupHelper.getGroup(groupId); return groupHelper.getGroup(groupId);
} }
@ -1063,8 +1062,9 @@ public class ManagerImpl implements Manager {
final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId()); final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId());
return new Identity(address, return new Identity(address,
identityInfo.getIdentityKey(), identityInfo.getIdentityKey(),
computeSafetyNumber(address.toSignalServiceAddress(), identityInfo.getIdentityKey()), identityHelper.computeSafetyNumber(identityInfo.getRecipientId(), identityInfo.getIdentityKey()),
computeSafetyNumberForScanning(address.toSignalServiceAddress(), identityInfo.getIdentityKey()), identityHelper.computeSafetyNumberForScanning(identityInfo.getRecipientId(),
identityInfo.getIdentityKey()).getSerialized(),
identityInfo.getTrustLevel(), identityInfo.getTrustLevel(),
identityInfo.getDateAdded()); identityInfo.getDateAdded());
} }
@ -1094,9 +1094,7 @@ public class ManagerImpl implements Manager {
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
return false; return false;
} }
return trustIdentity(recipientId, return identityHelper.trustIdentityVerified(recipientId, fingerprint);
identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
TrustLevel.TRUSTED_VERIFIED);
} }
/** /**
@ -1113,10 +1111,7 @@ public class ManagerImpl implements Manager {
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
return false; return false;
} }
var address = resolveSignalServiceAddress(recipientId); return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber);
return trustIdentity(recipientId,
identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)),
TrustLevel.TRUSTED_VERIFIED);
} }
/** /**
@ -1133,15 +1128,7 @@ public class ManagerImpl implements Manager {
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
return false; return false;
} }
var address = resolveSignalServiceAddress(recipientId); return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber);
return trustIdentity(recipientId, identityKey -> {
final var fingerprint = computeSafetyNumberFingerprint(address, identityKey);
try {
return fingerprint != null && fingerprint.getScannableFingerprint().compareTo(safetyNumber);
} catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
return false;
}
}, TrustLevel.TRUSTED_VERIFIED);
} }
/** /**
@ -1157,66 +1144,13 @@ public class ManagerImpl implements Manager {
} catch (UnregisteredUserException e) { } catch (UnregisteredUserException e) {
return false; return false;
} }
return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); return identityHelper.trustIdentityAllKeys(recipientId);
}
private boolean trustIdentity(
RecipientId recipientId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
) {
var identity = account.getIdentityKeyStore().getIdentity(recipientId);
if (identity == null) {
return false;
}
if (!verifier.apply(identity.getIdentityKey())) {
return false;
}
account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel);
try {
var address = resolveSignalServiceAddress(recipientId);
syncHelper.sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
} catch (IOException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage());
}
return true;
} }
private void handleIdentityFailure( private void handleIdentityFailure(
final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure
) { ) {
final var identityKey = identityFailure.getIdentityKey(); this.identityHelper.handleIdentityFailure(recipientId, identityFailure);
if (identityKey != null) {
final var newIdentity = account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
if (newIdentity) {
account.getSessionStore().archiveSessions(recipientId);
}
} else {
// Retrieve profile to get the current identity key from the server
profileHelper.refreshRecipientProfile(recipientId);
}
}
@Override
public String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) {
final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey);
return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
}
private byte[] computeSafetyNumberForScanning(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey) {
final Fingerprint fingerprint = computeSafetyNumberFingerprint(theirAddress, theirIdentityKey);
return fingerprint == null ? null : fingerprint.getScannableFingerprint().getSerialized();
}
private Fingerprint computeSafetyNumberFingerprint(
final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
) {
return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(),
account.getIdentityKeyPair().getPublicKey(),
theirAddress,
theirIdentityKey);
} }
@Override @Override
@ -1291,5 +1225,4 @@ public class ManagerImpl implements Manager {
} }
account = null; account = null;
} }
} }

View file

@ -0,0 +1,135 @@
package org.asamk.signal.manager.helper;
import org.asamk.signal.manager.SignalDependencies;
import org.asamk.signal.manager.TrustLevel;
import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.util.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.fingerprint.Fingerprint;
import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
import org.whispersystems.libsignal.fingerprint.ScannableFingerprint;
import org.whispersystems.signalservice.api.messages.SendMessageResult;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import java.io.IOException;
import java.util.Arrays;
import java.util.Date;
import java.util.function.Function;
import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
public class IdentityHelper {
private final static Logger logger = LoggerFactory.getLogger(IdentityHelper.class);
private final SignalAccount account;
private final SignalDependencies dependencies;
private final SignalServiceAddressResolver addressResolver;
private final SyncHelper syncHelper;
private final ProfileHelper profileHelper;
public IdentityHelper(
final SignalAccount account,
final SignalDependencies dependencies,
final SignalServiceAddressResolver addressResolver,
final SyncHelper syncHelper,
final ProfileHelper profileHelper
) {
this.account = account;
this.dependencies = dependencies;
this.addressResolver = addressResolver;
this.syncHelper = syncHelper;
this.profileHelper = profileHelper;
}
public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) {
return trustIdentity(recipientId,
identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
TrustLevel.TRUSTED_VERIFIED);
}
public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
return trustIdentity(recipientId,
identityKey -> safetyNumber.equals(computeSafetyNumber(recipientId, identityKey)),
TrustLevel.TRUSTED_VERIFIED);
}
public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
return trustIdentity(recipientId, identityKey -> {
final var fingerprint = computeSafetyNumberForScanning(recipientId, identityKey);
try {
return fingerprint != null && fingerprint.compareTo(safetyNumber);
} catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
return false;
}
}, TrustLevel.TRUSTED_VERIFIED);
}
public boolean trustIdentityAllKeys(RecipientId recipientId) {
return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
}
public String computeSafetyNumber(RecipientId recipientId, IdentityKey theirIdentityKey) {
var address = addressResolver.resolveSignalServiceAddress(recipientId);
final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
}
public ScannableFingerprint computeSafetyNumberForScanning(RecipientId recipientId, IdentityKey theirIdentityKey) {
var address = addressResolver.resolveSignalServiceAddress(recipientId);
final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
return fingerprint == null ? null : fingerprint.getScannableFingerprint();
}
private Fingerprint computeSafetyNumberFingerprint(
final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
) {
return Utils.computeSafetyNumber(capabilities.isUuid(),
account.getSelfAddress(),
account.getIdentityKeyPair().getPublicKey(),
theirAddress,
theirIdentityKey);
}
private boolean trustIdentity(
RecipientId recipientId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
) {
var identity = account.getIdentityKeyStore().getIdentity(recipientId);
if (identity == null) {
return false;
}
if (!verifier.apply(identity.getIdentityKey())) {
return false;
}
account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel);
try {
var address = addressResolver.resolveSignalServiceAddress(recipientId);
syncHelper.sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
} catch (IOException e) {
logger.warn("Failed to send verification sync message: {}", e.getMessage());
}
return true;
}
public void handleIdentityFailure(
final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure
) {
final var identityKey = identityFailure.getIdentityKey();
if (identityKey != null) {
final var newIdentity = account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
if (newIdentity) {
account.getSessionStore().archiveSessions(recipientId);
}
} else {
// Retrieve profile to get the current identity key from the server
profileHelper.refreshRecipientProfile(recipientId);
}
}
}

View file

@ -6,7 +6,6 @@ import org.asamk.signal.manager.api.RecipientIdentifier;
import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupId;
import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.manager.groups.GroupUtils;
import org.asamk.signal.util.DateUtils; import org.asamk.signal.util.DateUtils;
import org.asamk.signal.util.Util;
import org.slf4j.helpers.MessageFormatter; import org.slf4j.helpers.MessageFormatter;
import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage;
import org.whispersystems.signalservice.api.messages.SignalServiceAttachment; import org.whispersystems.signalservice.api.messages.SignalServiceAttachment;
@ -377,9 +376,6 @@ public class ReceiveMessageHandler implements Manager.ReceiveMessageHandler {
writer.println("Received sync message with verified identities:"); writer.println("Received sync message with verified identities:");
final var verifiedMessage = syncMessage.getVerified().get(); final var verifiedMessage = syncMessage.getVerified().get();
writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified()); writer.println("- {}: {}", formatContact(verifiedMessage.getDestination()), verifiedMessage.getVerified());
var safetyNumber = Util.formatSafetyNumber(m.computeSafetyNumber(verifiedMessage.getDestination(),
verifiedMessage.getIdentityKey()));
writer.indentedWriter().println(safetyNumber);
} }
if (syncMessage.getConfiguration().isPresent()) { if (syncMessage.getConfiguration().isPresent()) {
writer.println("Received sync message with configuration:"); writer.println("Received sync message with configuration:");

View file

@ -30,7 +30,6 @@ import org.freedesktop.dbus.DBusPath;
import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.connections.impl.DBusConnection;
import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.exceptions.DBusException;
import org.freedesktop.dbus.interfaces.DBusInterface; import org.freedesktop.dbus.interfaces.DBusInterface;
import org.whispersystems.libsignal.IdentityKey;
import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.InvalidKeyException;
import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional; import org.whispersystems.libsignal.util.guava.Optional;
@ -538,13 +537,6 @@ public class DbusManagerImpl implements Manager {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override
public String computeSafetyNumber(
final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
) {
throw new UnsupportedOperationException();
}
@Override @Override
public SignalServiceAddress resolveSignalServiceAddress(final SignalServiceAddress address) { public SignalServiceAddress resolveSignalServiceAddress(final SignalServiceAddress address) {
return address; return address;