]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java
Update dependencies
[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 Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
62 return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
63 }
64
65 public ScannableFingerprint computeSafetyNumberForScanning(ServiceId serviceId, IdentityKey theirIdentityKey) {
66 final Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
67 return fingerprint == null ? null : fingerprint.getScannableFingerprint();
68 }
69
70 private Fingerprint computeSafetyNumberFingerprint(
71 final ServiceId serviceId, final IdentityKey theirIdentityKey
72 ) {
73 final var recipientId = account.getRecipientResolver().resolveRecipient(serviceId);
74 final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
75
76 if (false) {
77 if (serviceId.isUnknown()) {
78 return null;
79 }
80 return Utils.computeSafetyNumberForUuid(account.getAci(),
81 account.getAciIdentityKeyPair().getPublicKey(),
82 serviceId,
83 theirIdentityKey);
84 }
85 if (address.number().isEmpty()) {
86 return null;
87 }
88 return Utils.computeSafetyNumberForNumber(account.getNumber(),
89 account.getAciIdentityKeyPair().getPublicKey(),
90 address.number().get(),
91 theirIdentityKey);
92 }
93
94 private boolean trustIdentity(
95 RecipientId recipientId, BiFunction<ServiceId, IdentityKey, Boolean> verifier, TrustLevel trustLevel
96 ) {
97 final var address = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId);
98 final var serviceId = address.serviceId().orElse(null);
99 if (serviceId == null) {
100 return false;
101 }
102 var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
103 if (identity == null) {
104 return false;
105 }
106
107 if (!verifier.apply(serviceId, identity.getIdentityKey())) {
108 return false;
109 }
110
111 account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel);
112 try {
113 context.getSyncHelper()
114 .sendVerifiedMessage(address.toSignalServiceAddress(), identity.getIdentityKey(), trustLevel);
115 } catch (IOException e) {
116 logger.warn("Failed to send verification sync message: {}", e.getMessage());
117 }
118
119 return true;
120 }
121
122 public void handleIdentityFailure(
123 final RecipientId recipientId,
124 final ServiceId serviceId,
125 final SendMessageResult.IdentityFailure identityFailure
126 ) {
127 final var identityKey = identityFailure.getIdentityKey();
128 if (identityKey != null) {
129 account.getIdentityKeyStore().saveIdentity(serviceId, identityKey);
130 } else {
131 // Retrieve profile to get the current identity key from the server
132 context.getProfileHelper().refreshRecipientProfile(recipientId);
133 }
134 }
135 }