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