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