]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/IdentityHelper.java
Update libsignal-service-java
[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.RecipientAddress;
6 import org.asamk.signal.manager.storage.recipients.RecipientId;
7 import org.asamk.signal.manager.util.Utils;
8 import org.signal.libsignal.protocol.IdentityKey;
9 import org.signal.libsignal.protocol.fingerprint.Fingerprint;
10 import org.signal.libsignal.protocol.fingerprint.FingerprintParsingException;
11 import org.signal.libsignal.protocol.fingerprint.FingerprintVersionMismatchException;
12 import org.signal.libsignal.protocol.fingerprint.ScannableFingerprint;
13 import org.slf4j.Logger;
14 import org.slf4j.LoggerFactory;
15 import org.whispersystems.signalservice.api.messages.SendMessageResult;
16 import org.whispersystems.signalservice.api.push.ServiceId;
17
18 import java.io.IOException;
19 import java.util.Arrays;
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 final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
38 return trustIdentity(serviceId,
39 identityKey -> Arrays.equals(identityKey.serialize(), fingerprint),
40 TrustLevel.TRUSTED_VERIFIED);
41 }
42
43 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, String safetyNumber) {
44 final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
45 return trustIdentity(serviceId,
46 identityKey -> safetyNumber.equals(computeSafetyNumber(serviceId, identityKey)),
47 TrustLevel.TRUSTED_VERIFIED);
48 }
49
50 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId, byte[] safetyNumber) {
51 final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
52 return trustIdentity(serviceId, identityKey -> {
53 final var fingerprint = computeSafetyNumberForScanning(serviceId, identityKey);
54 try {
55 return fingerprint != null && fingerprint.compareTo(safetyNumber);
56 } catch (FingerprintVersionMismatchException | FingerprintParsingException e) {
57 return false;
58 }
59 }, TrustLevel.TRUSTED_VERIFIED);
60 }
61
62 public boolean trustIdentityAllKeys(RecipientId recipientId) {
63 final var serviceId = account.getRecipientAddressResolver().resolveRecipientAddress(recipientId).getServiceId();
64 return trustIdentity(serviceId, identityKey -> true, TrustLevel.TRUSTED_UNVERIFIED);
65 }
66
67 public String computeSafetyNumber(ServiceId serviceId, IdentityKey theirIdentityKey) {
68 final Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
69 return fingerprint == null ? null : fingerprint.getDisplayableFingerprint().getDisplayText();
70 }
71
72 public ScannableFingerprint computeSafetyNumberForScanning(ServiceId serviceId, IdentityKey theirIdentityKey) {
73 final Fingerprint fingerprint = computeSafetyNumberFingerprint(serviceId, theirIdentityKey);
74 return fingerprint == null ? null : fingerprint.getScannableFingerprint();
75 }
76
77 private Fingerprint computeSafetyNumberFingerprint(
78 final ServiceId serviceId, final IdentityKey theirIdentityKey
79 ) {
80 final var address = account.getRecipientAddressResolver()
81 .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId));
82
83 return Utils.computeSafetyNumber(capabilities.getUuid(),
84 account.getSelfRecipientAddress(),
85 account.getAciIdentityKeyPair().getPublicKey(),
86 address.getServiceId().equals(serviceId)
87 ? address
88 : new RecipientAddress(serviceId, address.number().orElse(null)),
89 theirIdentityKey);
90 }
91
92 private boolean trustIdentity(
93 ServiceId serviceId, Function<IdentityKey, Boolean> verifier, TrustLevel trustLevel
94 ) {
95 var identity = account.getIdentityKeyStore().getIdentityInfo(serviceId);
96 if (identity == null) {
97 return false;
98 }
99
100 if (!verifier.apply(identity.getIdentityKey())) {
101 return false;
102 }
103
104 account.getIdentityKeyStore().setIdentityTrustLevel(serviceId, identity.getIdentityKey(), trustLevel);
105 try {
106 final var address = account.getRecipientAddressResolver()
107 .resolveRecipientAddress(account.getRecipientResolver().resolveRecipient(serviceId))
108 .toSignalServiceAddress();
109 context.getSyncHelper().sendVerifiedMessage(address, identity.getIdentityKey(), trustLevel);
110 } catch (IOException e) {
111 logger.warn("Failed to send verification sync message: {}", e.getMessage());
112 }
113
114 return true;
115 }
116
117 public void handleIdentityFailure(
118 final RecipientId recipientId,
119 final ServiceId serviceId,
120 final SendMessageResult.IdentityFailure identityFailure
121 ) {
122 final var identityKey = identityFailure.getIdentityKey();
123 if (identityKey != null) {
124 account.getIdentityKeyStore().saveIdentity(serviceId, identityKey);
125 } else {
126 // Retrieve profile to get the current identity key from the server
127 context.getProfileHelper().refreshRecipientProfile(recipientId);
128 }
129 }
130 }