]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java
Fix inspections
[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.ServiceId;
16
17 import java.util.Arrays;
18 import java.util.function.BiFunction;
19
20 public class IdentityHelper {
21
22 private final static Logger logger = LoggerFactory.getLogger(IdentityHelper.class);
23
24 private final SignalAccount account;
25 private final Context context;
26
27 public IdentityHelper(final Context context) {
28 this.account = context.getAccount();
29 this.context = context;
30 }
31
32 public boolean trustIdentityVerified(RecipientId recipientId, byte[] fingerprint) {
33 return trustIdentity(recipientId,
34 (serviceId, identityKey) -> Arrays.equals(identityKey.serialize(), fingerprint),
35 TrustLevel.TRUSTED_VERIFIED);
36 }
37
38 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
39 return trustIdentity(recipientId,
40 (serviceId, identityKey) -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)),
41 TrustLevel.TRUSTED_VERIFIED);
42 }
43
44 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
45 return trustIdentity(recipientId, (serviceId, identityKey) -> {
46 final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey);
47 try {
48 return fingerprint != null && fingerprint.compareTo(safetyNumber);
49 } catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
50 return false;
51 }
52 }, TrustLevel.TRUSTED_VERIFIED);
53 }
54
55 public boolean trustIdentityAllKeys(RecipientId recipientId) {
56 return trustIdentity(recipientId, (serviceId, identityKey) -> true, TrustLevel.TRUSTED_UNVERIFIED);
57 }
58
59 public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) {
60 final var fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey, false);
61 return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
62 }
63
64 public ScannableFingerprint computeSafetyNumberForScanning(ServiceId serviceId, IdentityKey theirIdentityKey) {
65 var fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey, false);
66 if (fingerprint == null) {
67 fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey, true);
68 }
69 return fingerprint == null ? null : fingerprint.getScannableFingerprint();
70 }
71
72 private Fingerprint computeSafetyNumberFingerprint(
73 final ServiceId serviceId, final IdentityKey theirIdentityKey, boolean useServiceId
74 ) {
75 final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
76 final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
77
78 if (useServiceId) {
79 if (serviceId.isUnknown()) {
80 return null;
81 }
82 return Utils.computeSafetyNumberForUuid(account.getAci(),
83 account.getAciIdentityKeyPair().getPublicKey(),
84 serviceId,
85 theirIdentityKey);
86 }
87 if (address.number().isEmpty()) {
88 return null;
89 }
90 return Utils.computeSafetyNumberForNumber(account.getNumber(),
91 account.getAciIdentityKeyPair().getPublicKey(),
92 address.number().get(),
93 theirIdentityKey);
94 }
95
96 private boolean trustIdentity(
97 RecipientId recipientId, BiFunction<ServiceId, IdentityKey, Boolean> verifier, TrustLevel trustLevel
98 ) {
99 final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
100 final var serviceId = address.serviceId().orElse(null);
101 if (serviceId == null) {
102 return false;
103 }
104 var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
105 if (identity == null) {
106 return false;
107 }
108
109 if (!verifier.apply(serviceId, identity.getIdentityKey())) {
110 return false;
111 }
112
113 account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel);
114 context.getSyncHelper()
115 .sendVerifiedMessage(address.toSignalServiceAddress(), identity.getIdentityKey(), trustLevel);
116
117 return true;
118 }
119
120 public void handleIdentityFailure(
121 final RecipientId recipientId,
122 final ServiceId serviceId,
123 final SendMessageResult.IdentityFailure identityFailure
124 ) {
125 final var identityKey = identityFailure.getIdentityKey();
126 if (identityKey != null) {
127 account.getIdentityKeyStore().saveIdentity(serviceId, identityKey);
128 } else {
129 // Retrieve profile to get the current identity key from the server
130 context.getProfileHelper().refreshRecipientProfile(recipientId);
131 }
132 }
133 }