1 package org
.asamk
.signal
.manager
.helper
;
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
;
17 import java
.io
.IOException
;
18 import java
.util
.Arrays
;
19 import java
.util
.function
.BiFunction
;
21 public class IdentityHelper
{
23 private final static Logger logger
= LoggerFactory
.getLogger(IdentityHelper
.class);
25 private final SignalAccount account
;
26 private final Context context
;
28 public IdentityHelper(final Context context
) {
29 this.account
= context
.getAccount();
30 this.context
= context
;
33 public boolean trustIdentityVerified(RecipientId recipientId
, byte[] fingerprint
) {
34 return trustIdentity(recipientId
,
35 (serviceId
, identityKey
) -> Arrays
.equals(identityKey
.serialize(), fingerprint
),
36 TrustLevel
.TRUSTED_VERIFIED
);
39 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId
, String safetyNumber
) {
40 return trustIdentity(recipientId
,
41 (serviceId
, identityKey
) -> safetyNumber
.equals(computeSafetyNumber(serviceId
, identityKey
)),
42 TrustLevel
.TRUSTED_VERIFIED
);
45 public boolean trustIdentityVerifiedSafetyNumber(RecipientId recipientId
, byte[] safetyNumber
) {
46 return trustIdentity(recipientId
, (serviceId
, identityKey
) -> {
47 final var fingerprint
= computeSafetyNumberForScanning(serviceId
, identityKey
);
49 return fingerprint
!= null && fingerprint
.compareTo(safetyNumber
);
50 } catch (FingerprintVersionMismatchException
| FingerprintParsingException e
) {
53 }, TrustLevel
.TRUSTED_VERIFIED
);
56 public boolean trustIdentityAllKeys(RecipientId recipientId
) {
57 return trustIdentity(recipientId
, (serviceId
, identityKey
) -> true, TrustLevel
.TRUSTED_UNVERIFIED
);
60 public String
computeSafetyNumber(ServiceId serviceId
, IdentityKey theirIdentityKey
) {
61 final Fingerprint fingerprint
= computeSafetyNumberFingerprint(serviceId
, theirIdentityKey
);
62 return fingerprint
== null ?
null : fingerprint
.getDisplayableFingerprint().getDisplayText();
65 public ScannableFingerprint
computeSafetyNumberForScanning(ServiceId serviceId
, IdentityKey theirIdentityKey
) {
66 final Fingerprint fingerprint
= computeSafetyNumberFingerprint(serviceId
, theirIdentityKey
);
67 return fingerprint
== null ?
null : fingerprint
.getScannableFingerprint();
70 private Fingerprint
computeSafetyNumberFingerprint(
71 final ServiceId serviceId
, final IdentityKey theirIdentityKey
73 final var recipientId
= account
.getRecipientResolver().resolveRecipient(serviceId
);
74 final var address
= account
.getRecipientAddressResolver().resolveRecipientAddress(recipientId
);
76 if (account
.getAccountCapabilities().getUuid()) {
77 if (serviceId
.isUnknown()) {
80 return Utils
.computeSafetyNumberForUuid(account
.getAci(),
81 account
.getAciIdentityKeyPair().getPublicKey(),
85 if (address
.number().isEmpty()) {
88 return Utils
.computeSafetyNumberForNumber(account
.getNumber(),
89 account
.getAciIdentityKeyPair().getPublicKey(),
90 address
.number().get(),
94 private boolean trustIdentity(
95 RecipientId recipientId
, BiFunction
<ServiceId
, IdentityKey
, Boolean
> verifier
, TrustLevel trustLevel
97 final var serviceId
= account
.getRecipientAddressResolver()
98 .resolveRecipientAddress(recipientId
)
101 if (serviceId
== null) {
104 var identity
= account
.getIdentityKeyStore().getIdentityInfo(serviceId
);
105 if (identity
== null) {
109 if (!verifier
.apply(serviceId
, identity
.getIdentityKey())) {
113 account
.getIdentityKeyStore().setIdentityTrustLevel(serviceId
, identity
.getIdentityKey(), trustLevel
);
115 final var address
= context
.getRecipientHelper()
116 .resolveSignalServiceAddress(account
.getRecipientResolver().resolveRecipient(serviceId
));
117 context
.getSyncHelper().sendVerifiedMessage(address
, identity
.getIdentityKey(), trustLevel
);
118 } catch (IOException e
) {
119 logger
.warn("Failed to send verification sync message: {}", e
.getMessage());
125 public void handleIdentityFailure(
126 final RecipientId recipientId
,
127 final ServiceId serviceId
,
128 final SendMessageResult
.IdentityFailure identityFailure
130 final var identityKey
= identityFailure
.getIdentityKey();
131 if (identityKey
!= null) {
132 account
.getIdentityKeyStore().saveIdentity(serviceId
, identityKey
);
134 // Retrieve profile to get the current identity key from the server
135 context
.getProfileHelper().refreshRecipientProfile(recipientId
);