]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java
531870d9e63f51906d966c5af05afd4b578a014c
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / IdentityHelper.java
1 package org.asamk.signal.manager.helper;
2
3 import org.asamk.signal.manager.SignalDependencies;
4 import org.asamk.signal.manager.TrustLevel;
5 import org.asamk.signal.manager.storage.SignalAccount;
6 import org.asamk.signal.manager.storage.recipients.RecipientId;
7 import org.asamk.signal.manager.util.Utils;
8 import org.slf4j.Logger;
9 import org.slf4j.LoggerFactory;
10 import org.whispersystems.libsignal.IdentityKey;
11 import org.whispersystems.libsignal.fingerprint.Fingerprint;
12 import org.whispersystems.libsignal.fingerprint.FingerprintParsingException;
13 import org.whispersystems.libsignal.fingerprint.FingerprintVersionMismatchException;
14 import org.whispersystems.libsignal.fingerprint.ScannableFingerprint;
15 import org.whispersystems.signalservice.api.messages.SendMessageResult;
16 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
17
18 import java.io.IOException;
19 import java.util.Arrays;
20 import java.util.Date;
21 import java.util.function.Function;
22
23 import static org.asamk.signal.manager.config.ServiceConfig.capabilities;
24
25 public class IdentityHelper {
26
27 private final static Logger logger = LoggerFactory.getLogger(IdentityHelper.class);
28
29 private final SignalAccount account;
30 private final SignalDependencies dependencies;
31 private final SignalServiceAddressResolver addressResolver;
32 private final SyncHelper syncHelper;
33 private final ProfileHelper profileHelper;
34
35 public IdentityHelper(
36 final SignalAccount account,
37 final SignalDependencies dependencies,
38 final SignalServiceAddressResolver addressResolver,
39 final SyncHelper syncHelper,
40 final ProfileHelper profileHelper
41 ) {
42 this.account = account;
43 this.dependencies = dependencies;
44 this.addressResolver = addressResolver;
45 this.syncHelper = syncHelper;
46 this.profileHelper = profileHelper;
47 }
48
49 public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) {
50 return trustIdentity(recipientId,
51 identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
52 TrustLevel.TRUSTED_VERIFIED);
53 }
54
55 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
56 return trustIdentity(recipientId,
57 identityKey -> safetyNumber.equals(computeSafetyNumber(recipientId, identityKey)),
58 TrustLevel.TRUSTED_VERIFIED);
59 }
60
61 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
62 return trustIdentity(recipientId, identityKey -> {
63 final var fingerprint = computeSafetyNumberForScanning(recipientId, identityKey);
64 try {
65 return fingerprint != null && fingerprint.compareTo(safetyNumber);
66 } catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
67 return false;
68 }
69 }, TrustLevel.TRUSTED_VERIFIED);
70 }
71
72 public boolean trustIdentityAllKeys(RecipientId recipientId) {
73 return trustIdentity(recipientId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
74 }
75
76 public String computeSafetyNumber(RecipientId recipientId, IdentityKey theirIdentityKey) {
77 var address = addressResolver.resolveSignalServiceAddress(recipientId);
78 final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
79 return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
80 }
81
82 public ScannableFingerprint computeSafetyNumberForScanning(RecipientId recipientId, IdentityKey theirIdentityKey) {
83 var address = addressResolver.resolveSignalServiceAddress(recipientId);
84 final Fingerprint fingerprint = computeSafetyNumberFingerprint(address, theirIdentityKey);
85 return fingerprint == null ? null : fingerprint.getScannableFingerprint();
86 }
87
88 private Fingerprint computeSafetyNumberFingerprint(
89 final SignalServiceAddress theirAddress, final IdentityKey theirIdentityKey
90 ) {
91 return Utils.computeSafetyNumber(capabilities.isUuid(),
92 account.getSelfAddress(),
93 account.getIdentityKeyPair().getPublicKey(),
94 theirAddress,
95 theirIdentityKey);
96 }
97
98 private boolean trustIdentity(
99 RecipientId recipientId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
100 ) {
101 var identity = account.getIdentityKeyStore().getIdentity(recipientId);
102 if (identity == null) {
103 return false;
104 }
105
106 if (!verifier.apply(identity.getIdentityKey())) {
107 return false;
108 }
109
110 account.getIdentityKeyStore().setIdentityTrustLevel(recipientId, identity.getIdentityKey(), trustLevel);
111 try {
112 var address = addressResolver.resolveSignalServiceAddress(recipientId);
113 syncHelper.sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
114 } catch (IOException e) {
115 logger.warn("Failed to send verification sync message: {}", e.getMessage());
116 }
117
118 return true;
119 }
120
121 public void handleIdentityFailure(
122 final RecipientId recipientId, final SendMessageResult.IdentityFailure identityFailure
123 ) {
124 final var identityKey = identityFailure.getIdentityKey();
125 if (identityKey != null) {
126 final var newIdentity = account.getIdentityKeyStore().saveIdentity(recipientId, identityKey, new Date());
127 if (newIdentity) {
128 account.getSessionStore().archiveSessions(recipientId);
129 }
130 } else {
131 // Retrieve profile to get the current identity key from the server
132 profileHelper.refreshRecipientProfile(recipientId);
133 }
134 }
135 }