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
.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
;
18 import java
.io
.IOException
;
19 import java
.util
.Arrays
;
20 import java
.util
.function
.Function
;
22 import static org
.asamk
.signal
.manager
.config
.ServiceConfig
.capabilities
;
24 public class IdentityHelper
{
26 private final static Logger logger
= LoggerFactory
.getLogger(IdentityHelper
.class);
28 private final SignalAccount account
;
29 private final Context context
;
31 public IdentityHelper(final Context context
) {
32 this.account
= context
.getAccount();
33 this.context
= context
;
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
);
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
);
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
);
55 return fingerprint
!= null && fingerprint
.compareTo(safetyNumber
);
56 } catch (FingerprintVersionMismatchException
| FingerprintParsingException e
) {
59 }, TrustLevel
.TRUSTED_VERIFIED
);
62 public boolean trustIdentityAllKeys(RecipientId recipientId
) {
63 final var serviceId
= account
.getRecipientAddressResolver().resolveRecipientAddress(recipientId
).getServiceId();
64 return trustIdentity(serviceId
, identityKey
-> true, TrustLevel
.TRUSTED_UNVERIFIED
);
67 public String
computeSafetyNumber(ServiceId serviceId
, IdentityKey theirIdentityKey
) {
68 final Fingerprint fingerprint
= computeSafetyNumberFingerprint(serviceId
, theirIdentityKey
);
69 return fingerprint
== null ?
null : fingerprint
.getDisplayableFingerprint().getDisplayText();
72 public ScannableFingerprint
computeSafetyNumberForScanning(ServiceId serviceId
, IdentityKey theirIdentityKey
) {
73 final Fingerprint fingerprint
= computeSafetyNumberFingerprint(serviceId
, theirIdentityKey
);
74 return fingerprint
== null ?
null : fingerprint
.getScannableFingerprint();
77 private Fingerprint
computeSafetyNumberFingerprint(
78 final ServiceId serviceId
, final IdentityKey theirIdentityKey
80 final var address
= account
.getRecipientAddressResolver()
81 .resolveRecipientAddress(account
.getRecipientResolver().resolveRecipient(serviceId
));
83 return Utils
.computeSafetyNumber(capabilities
.getUuid(),
84 account
.getSelfRecipientAddress(),
85 account
.getAciIdentityKeyPair().getPublicKey(),
86 address
.getServiceId().equals(serviceId
)
88 : new RecipientAddress(serviceId
, address
.number().orElse(null)),
92 private boolean trustIdentity(
93 ServiceId serviceId
, Function
<IdentityKey
, Boolean
> verifier
, TrustLevel trustLevel
95 var identity
= account
.getIdentityKeyStore().getIdentityInfo(serviceId
);
96 if (identity
== null) {
100 if (!verifier
.apply(identity
.getIdentityKey())) {
104 account
.getIdentityKeyStore().setIdentityTrustLevel(serviceId
, identity
.getIdentityKey(), trustLevel
);
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());
117 public void handleIdentityFailure(
118 final RecipientId recipientId
,
119 final ServiceId serviceId
,
120 final SendMessageResult
.IdentityFailure identityFailure
122 final var identityKey
= identityFailure
.getIdentityKey();
123 if (identityKey
!= null) {
124 account
.getIdentityKeyStore().saveIdentity(serviceId
, identityKey
);
126 // Retrieve profile to get the current identity key from the server
127 context
.getProfileHelper().refreshRecipientProfile(recipientId
);