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
.RecipientId
;
7 import org
.signal
.libsignal
.metadata
.certificate
.InvalidCertificateException
;
8 import org
.slf4j
.Logger
;
9 import org
.slf4j
.LoggerFactory
;
10 import org
.whispersystems
.libsignal
.util
.guava
.Optional
;
11 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccess
;
12 import org
.whispersystems
.signalservice
.api
.crypto
.UnidentifiedAccessPair
;
14 import java
.io
.IOException
;
15 import java
.util
.List
;
16 import java
.util
.stream
.Collectors
;
18 import static org
.whispersystems
.signalservice
.internal
.util
.Util
.getSecretBytes
;
20 public class UnidentifiedAccessHelper
{
22 private final static Logger logger
= LoggerFactory
.getLogger(UnidentifiedAccessHelper
.class);
24 private final SignalAccount account
;
25 private final SignalDependencies dependencies
;
26 private final SelfProfileKeyProvider selfProfileKeyProvider
;
27 private final ProfileProvider profileProvider
;
29 public UnidentifiedAccessHelper(
30 final SignalAccount account
,
31 final SignalDependencies dependencies
,
32 final SelfProfileKeyProvider selfProfileKeyProvider
,
33 final ProfileProvider profileProvider
35 this.account
= account
;
36 this.dependencies
= dependencies
;
37 this.selfProfileKeyProvider
= selfProfileKeyProvider
;
38 this.profileProvider
= profileProvider
;
41 private byte[] getSenderCertificateFor(final RecipientId recipientId
) {
42 final var sharingMode
= account
.getConfigurationStore().getPhoneNumberSharingMode();
43 if (sharingMode
== PhoneNumberSharingMode
.EVERYBODY
|| (
44 sharingMode
== PhoneNumberSharingMode
.CONTACTS
45 && account
.getContactStore().getContact(recipientId
) != null
47 logger
.debug("Using normal sender certificate for message to {}", recipientId
);
48 return getSenderCertificate();
50 logger
.debug("Using phone number privacy sender certificate for message to {}", recipientId
);
51 return getSenderCertificateForPhoneNumberPrivacy();
55 private byte[] getSenderCertificateForPhoneNumberPrivacy() {
56 // TODO cache for a day
58 return dependencies
.getAccountManager().getSenderCertificateForPhoneNumberPrivacy();
59 } catch (IOException e
) {
60 logger
.warn("Failed to get sender certificate, ignoring: {}", e
.getMessage());
65 private byte[] getSenderCertificate() {
66 // TODO cache for a day
68 return dependencies
.getAccountManager().getSenderCertificate();
69 } catch (IOException e
) {
70 logger
.warn("Failed to get sender certificate, ignoring: {}", e
.getMessage());
75 private byte[] getSelfUnidentifiedAccessKey() {
76 return UnidentifiedAccess
.deriveAccessKeyFrom(selfProfileKeyProvider
.getProfileKey());
79 public byte[] getTargetUnidentifiedAccessKey(RecipientId recipient
) {
80 var targetProfile
= profileProvider
.getProfile(recipient
);
81 if (targetProfile
== null) {
85 switch (targetProfile
.getUnidentifiedAccessMode()) {
87 var theirProfileKey
= account
.getProfileStore().getProfileKey(recipient
);
88 if (theirProfileKey
== null) {
92 return UnidentifiedAccess
.deriveAccessKeyFrom(theirProfileKey
);
94 return createUnrestrictedUnidentifiedAccess();
100 public Optional
<UnidentifiedAccessPair
> getAccessForSync() {
101 var selfUnidentifiedAccessKey
= getSelfUnidentifiedAccessKey();
102 var selfUnidentifiedAccessCertificate
= getSenderCertificate();
104 if (selfUnidentifiedAccessKey
== null || selfUnidentifiedAccessCertificate
== null) {
105 return Optional
.absent();
109 return Optional
.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey
,
110 selfUnidentifiedAccessCertificate
),
111 new UnidentifiedAccess(selfUnidentifiedAccessKey
, selfUnidentifiedAccessCertificate
)));
112 } catch (InvalidCertificateException e
) {
113 return Optional
.absent();
117 public List
<Optional
<UnidentifiedAccessPair
>> getAccessFor(List
<RecipientId
> recipients
) {
118 return recipients
.stream().map(this::getAccessFor
).collect(Collectors
.toList());
121 public Optional
<UnidentifiedAccessPair
> getAccessFor(RecipientId recipient
) {
122 var recipientUnidentifiedAccessKey
= getTargetUnidentifiedAccessKey(recipient
);
123 var selfUnidentifiedAccessKey
= getSelfUnidentifiedAccessKey();
124 var selfUnidentifiedAccessCertificate
= getSenderCertificateFor(recipient
);
126 if (recipientUnidentifiedAccessKey
== null
127 || selfUnidentifiedAccessKey
== null
128 || selfUnidentifiedAccessCertificate
== null) {
129 return Optional
.absent();
133 return Optional
.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey
,
134 selfUnidentifiedAccessCertificate
),
135 new UnidentifiedAccess(selfUnidentifiedAccessKey
, selfUnidentifiedAccessCertificate
)));
136 } catch (InvalidCertificateException e
) {
137 return Optional
.absent();
141 private static byte[] createUnrestrictedUnidentifiedAccess() {
142 return getSecretBytes(16);