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