From: AsamK Date: Tue, 12 Oct 2021 20:14:39 +0000 (+0200) Subject: Extract IdentityHelper X-Git-Tag: v0.9.1~10 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/f094cd6806aae68fba9ebd0cf29eaf4eabf042d2?ds=sidebyside Extract IdentityHelper --- diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index 3cfeb4f7..733e3dcc 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -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.Profile; import org.asamk.signal.manager.storage.recipients.RecipientAddress; -import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; @@ -228,8 +227,6 @@ public interface Manager extends Closeable { boolean trustIdentityAllKeys(RecipientIdentifier.Single recipient); - String computeSafetyNumber(SignalServiceAddress theirAddress, IdentityKey theirIdentityKey); - SignalServiceAddress resolveSignalServiceAddress(SignalServiceAddress address); @Override diff --git a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java index a14f2d1f..d2ffaaab 100644 --- a/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/ManagerImpl.java @@ -38,6 +38,7 @@ import org.asamk.signal.manager.helper.AttachmentHelper; import org.asamk.signal.manager.helper.ContactHelper; import org.asamk.signal.manager.helper.GroupHelper; 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.PinHelper; 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.util.KeyUtils; import org.asamk.signal.manager.util.StickerUtils; -import org.asamk.signal.manager.util.Utils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; 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.guava.Optional; import org.whispersystems.signalservice.api.SignalSessionLock; @@ -98,9 +94,7 @@ import java.net.URISyntaxException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.SignatureException; -import java.util.Arrays; import java.util.Collection; -import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -112,7 +106,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; import java.util.concurrent.locks.ReentrantLock; -import java.util.function.Function; import java.util.stream.Collectors; import static org.asamk.signal.manager.config.ServiceConfig.capabilities; @@ -138,6 +131,7 @@ public class ManagerImpl implements Manager { private final ContactHelper contactHelper; private final IncomingMessageHandler incomingMessageHandler; private final PreKeyHelper preKeyHelper; + private final IdentityHelper identityHelper; private final Context context; private boolean hasCaughtUpWithOldMessages = false; @@ -238,6 +232,11 @@ public class ManagerImpl implements Manager { syncHelper, this::getRecipientProfile, jobExecutor); + this.identityHelper = new IdentityHelper(account, + dependencies, + this::resolveSignalServiceAddress, + syncHelper, + profileHelper); } @Override @@ -1042,7 +1041,7 @@ public class ManagerImpl implements Manager { return toGroup(groupHelper.getGroup(groupId)); } - public GroupInfo getGroupInfo(GroupId groupId) { + private GroupInfo getGroupInfo(GroupId groupId) { return groupHelper.getGroup(groupId); } @@ -1063,8 +1062,9 @@ public class ManagerImpl implements Manager { final var address = account.getRecipientStore().resolveRecipientAddress(identityInfo.getRecipientId()); return new Identity(address, identityInfo.getIdentityKey(), - computeSafetyNumber(address.toSignalServiceAddress(), identityInfo.getIdentityKey()), - computeSafetyNumberForScanning(address.toSignalServiceAddress(), identityInfo.getIdentityKey()), + identityHelper.computeSafetyNumber(identityInfo.getRecipientId(), identityInfo.getIdentityKey()), + identityHelper.computeSafetyNumberForScanning(identityInfo.getRecipientId(), + identityInfo.getIdentityKey()).getSerialized(), identityInfo.getTrustLevel(), identityInfo.getDateAdded()); } @@ -1094,9 +1094,7 @@ public class ManagerImpl implements Manager { } catch (UnregisteredUserException e) { return false; } - return trustIdentity(recipientId, - identityKey -> Arrays.equals(identityKey.serialize(), fingerprint), - TrustLevel.TRUSTED_VERIFIED); + return identityHelper.trustIdentityVerified(recipientId, fingerprint); } /** @@ -1113,10 +1111,7 @@ public class ManagerImpl implements Manager { } catch (UnregisteredUserException e) { return false; } - var address = resolveSignalServiceAddress(recipientId); - return trustIdentity(recipientId, - identityKey -> safetyNumber.equals(computeSafetyNumber(address, identityKey)), - TrustLevel.TRUSTED_VERIFIED); + return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); } /** @@ -1133,15 +1128,7 @@ public class ManagerImpl implements Manager { } catch (UnregisteredUserException e) { return false; } - var address = resolveSignalServiceAddress(recipientId); - 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); + return identityHelper.trustIdentityVerifiedSafetyNumber(recipientId, safetyNumber); } /** @@ -1157,66 +1144,13 @@ public class ManagerImpl implements Manager { } catch (UnregisteredUserException e) { return false; } - return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED); - } - - private boolean trustIdentity( - RecipientId recipientId, Function 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; + return identityHelper.trustIdentityAllKeys(recipientId); } private 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); - } - } - - @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); + this.identityHelper.handleIdentityFailure(recipientId, identityFailure); } @Override @@ -1291,5 +1225,4 @@ public class ManagerImpl implements Manager { } account = null; } - } diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java new file mode 100644 index 00000000..531870d9 --- /dev/null +++ b/lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java @@ -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 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); + } + } +} diff --git a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java index 35790678..b1580b34 100644 --- a/src/main/java/org/asamk/signal/ReceiveMessageHandler.java +++ b/src/main/java/org/asamk/signal/ReceiveMessageHandler.java @@ -6,7 +6,6 @@ import org.asamk.signal.manager.api.RecipientIdentifier; import org.asamk.signal.manager.groups.GroupId; import org.asamk.signal.manager.groups.GroupUtils; import org.asamk.signal.util.DateUtils; -import org.asamk.signal.util.Util; import org.slf4j.helpers.MessageFormatter; import org.whispersystems.libsignal.protocol.DecryptionErrorMessage; 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:"); final var verifiedMessage = syncMessage.getVerified().get(); 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()) { writer.println("Received sync message with configuration:"); diff --git a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java index 6e655bdc..59422e69 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusManagerImpl.java @@ -30,7 +30,6 @@ import org.freedesktop.dbus.DBusPath; import org.freedesktop.dbus.connections.impl.DBusConnection; import org.freedesktop.dbus.exceptions.DBusException; import org.freedesktop.dbus.interfaces.DBusInterface; -import org.whispersystems.libsignal.IdentityKey; import org.whispersystems.libsignal.InvalidKeyException; import org.whispersystems.libsignal.util.Pair; import org.whispersystems.libsignal.util.guava.Optional; @@ -538,13 +537,6 @@ public class DbusManagerImpl implements Manager { throw new UnsupportedOperationException(); } - @Override - public String computeSafetyNumber( - final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey - ) { - throw new UnsupportedOperationException(); - } - @Override public SignalServiceAddress resolveSignalServiceAddress(final SignalServiceAddress address) { return address;