1 package org
.asamk
.signal
.manager
.helper
;
3 import org
.asamk
.signal
.manager
.SignalDependencies
;
4 import org
.asamk
.signal
.manager
.api
.PhoneNumberSharingMode
;
5 import org
.asamk
.signal
.manager
.storage
.SignalAccount
;
6 import org
.asamk
.signal
.manager
.storage
.recipients
.Profile
;
7 import org
.asamk
.signal
.manager
.storage
.recipients
.RecipientId
;
8 import org
.signal
.libsignal
.metadata
.certificate
.InvalidCertificateException
;
9 import org
.signal
.libsignal
.metadata
.certificate
.SenderCertificate
;
10 import org
.slf4j
.Logger
;
11 import org
.slf4j
.LoggerFactory
;
12 import org
.whispersystems
.libsignal
.util
.guava
.Optional
;
13 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccess
;
14 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccessPair
;
16 import java
.io
.IOException
;
17 import java
.util
.List
;
18 import java
.util
.concurrent
.TimeUnit
;
20 public class UnidentifiedAccessHelper
{
22 private final static Logger logger
= LoggerFactory
.getLogger(UnidentifiedAccessHelper
.class);
23 private final static long CERTIFICATE_EXPIRATION_BUFFER
= TimeUnit
.DAYS
.toMillis(1);
24 private static final byte[] UNRESTRICTED_KEY
= new byte[16];
26 private final SignalAccount account
;
27 private final SignalDependencies dependencies
;
28 private final SelfProfileKeyProvider selfProfileKeyProvider
;
29 private final ProfileProvider profileProvider
;
31 private SenderCertificate privacySenderCertificate
;
32 private SenderCertificate senderCertificate
;
34 public UnidentifiedAccessHelper(
35 final SignalAccount account
,
36 final SignalDependencies dependencies
,
37 final SelfProfileKeyProvider selfProfileKeyProvider
,
38 final ProfileProvider profileProvider
40 this.account
= account
;
41 this.dependencies
= dependencies
;
42 this.selfProfileKeyProvider
= selfProfileKeyProvider
;
43 this.profileProvider
= profileProvider
;
46 private byte[] getSenderCertificateFor(final RecipientId recipientId
) {
47 final var sharingMode
= account
.getConfigurationStore().getPhoneNumberSharingMode();
48 if (sharingMode
== PhoneNumberSharingMode
.EVERYBODY
|| (
49 sharingMode
== PhoneNumberSharingMode
.CONTACTS
50 && account
.getContactStore().getContact(recipientId
) != null
52 logger
.debug("Using normal sender certificate for message to {}", recipientId
);
53 return getSenderCertificate();
55 logger
.debug("Using phone number privacy sender certificate for message to {}", recipientId
);
56 return getSenderCertificateForPhoneNumberPrivacy();
60 private byte[] getSenderCertificateForPhoneNumberPrivacy() {
61 if (privacySenderCertificate
!= null && System
.currentTimeMillis() < (
62 privacySenderCertificate
.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER
64 return privacySenderCertificate
.getSerialized();
67 final var certificate
= dependencies
.getAccountManager().getSenderCertificateForPhoneNumberPrivacy();
68 privacySenderCertificate
= new SenderCertificate(certificate
);
70 } catch (IOException
| InvalidCertificateException e
) {
71 logger
.warn("Failed to get sender certificate, ignoring: {}", e
.getMessage());
76 private byte[] getSenderCertificate() {
77 if (senderCertificate
!= null && System
.currentTimeMillis() < (
78 senderCertificate
.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER
80 return senderCertificate
.getSerialized();
83 final var certificate
= dependencies
.getAccountManager().getSenderCertificate();
84 this.senderCertificate
= new SenderCertificate(certificate
);
86 } catch (IOException
| InvalidCertificateException e
) {
87 logger
.warn("Failed to get sender certificate, ignoring: {}", e
.getMessage());
92 private byte[] getSelfUnidentifiedAccessKey() {
93 var selfProfile
= profileProvider
.getProfile(account
.getSelfRecipientId());
94 if (selfProfile
!= null
95 && selfProfile
.getUnidentifiedAccessMode() == Profile
.UnidentifiedAccessMode
.UNRESTRICTED
) {
96 return createUnrestrictedUnidentifiedAccess();
98 return UnidentifiedAccess
.deriveAccessKeyFrom(selfProfileKeyProvider
.getProfileKey());
101 public byte[] getTargetUnidentifiedAccessKey(RecipientId recipient
) {
102 var targetProfile
= profileProvider
.getProfile(recipient
);
103 if (targetProfile
== null) {
107 switch (targetProfile
.getUnidentifiedAccessMode()) {
109 var theirProfileKey
= account
.getProfileStore().getProfileKey(recipient
);
110 if (theirProfileKey
== null) {
114 return UnidentifiedAccess
.deriveAccessKeyFrom(theirProfileKey
);
116 return createUnrestrictedUnidentifiedAccess();
122 public Optional
<UnidentifiedAccessPair
> getAccessForSync() {
123 var selfUnidentifiedAccessKey
= getSelfUnidentifiedAccessKey();
124 var selfUnidentifiedAccessCertificate
= getSenderCertificate();
126 if (selfUnidentifiedAccessKey
== null || selfUnidentifiedAccessCertificate
== null) {
127 return Optional
.absent();
131 return Optional
.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey
,
132 selfUnidentifiedAccessCertificate
),
133 new UnidentifiedAccess(selfUnidentifiedAccessKey
, selfUnidentifiedAccessCertificate
)));
134 } catch (InvalidCertificateException e
) {
135 return Optional
.absent();
139 public List
<Optional
<UnidentifiedAccessPair
>> getAccessFor(List
<RecipientId
> recipients
) {
140 return recipients
.stream().map(this::getAccessFor
).toList();
143 public Optional
<UnidentifiedAccessPair
> getAccessFor(RecipientId recipient
) {
144 var recipientUnidentifiedAccessKey
= getTargetUnidentifiedAccessKey(recipient
);
145 if (recipientUnidentifiedAccessKey
== null) {
146 return Optional
.absent();
149 var selfUnidentifiedAccessKey
= getSelfUnidentifiedAccessKey();
150 var selfUnidentifiedAccessCertificate
= getSenderCertificateFor(recipient
);
151 if (selfUnidentifiedAccessKey
== null || selfUnidentifiedAccessCertificate
== null) {
152 return Optional
.absent();
156 return Optional
.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey
,
157 selfUnidentifiedAccessCertificate
),
158 new UnidentifiedAccess(selfUnidentifiedAccessKey
, selfUnidentifiedAccessCertificate
)));
159 } catch (InvalidCertificateException e
) {
160 return Optional
.absent();
164 private static byte[] createUnrestrictedUnidentifiedAccess() {
165 return UNRESTRICTED_KEY
;