]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java
Read phone number sharing mode from storage
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / UnidentifiedAccessHelper.java
1 package org.asamk.signal.manager.helper;
2
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;
13
14 import java.io.IOException;
15 import java.util.List;
16 import java.util.stream.Collectors;
17
18 import static org.whispersystems.signalservice.internal.util.Util.getSecretBytes;
19
20 public class UnidentifiedAccessHelper {
21
22 private final static Logger logger = LoggerFactory.getLogger(UnidentifiedAccessHelper.class);
23
24 private final SignalAccount account;
25 private final SignalDependencies dependencies;
26 private final SelfProfileKeyProvider selfProfileKeyProvider;
27 private final ProfileProvider profileProvider;
28
29 public UnidentifiedAccessHelper(
30 final SignalAccount account,
31 final SignalDependencies dependencies,
32 final SelfProfileKeyProvider selfProfileKeyProvider,
33 final ProfileProvider profileProvider
34 ) {
35 this.account = account;
36 this.dependencies = dependencies;
37 this.selfProfileKeyProvider = selfProfileKeyProvider;
38 this.profileProvider = profileProvider;
39 }
40
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
46 )) {
47 logger.debug("Using normal sender certificate for message to {}", recipientId);
48 return getSenderCertificate();
49 } else {
50 logger.debug("Using phone number privacy sender certificate for message to {}", recipientId);
51 return getSenderCertificateForPhoneNumberPrivacy();
52 }
53 }
54
55 private byte[] getSenderCertificateForPhoneNumberPrivacy() {
56 // TODO cache for a day
57 try {
58 return dependencies.getAccountManager().getSenderCertificateForPhoneNumberPrivacy();
59 } catch (IOException e) {
60 logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
61 return null;
62 }
63 }
64
65 private byte[] getSenderCertificate() {
66 // TODO cache for a day
67 try {
68 return dependencies.getAccountManager().getSenderCertificate();
69 } catch (IOException e) {
70 logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
71 return null;
72 }
73 }
74
75 private byte[] getSelfUnidentifiedAccessKey() {
76 return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey());
77 }
78
79 public byte[] getTargetUnidentifiedAccessKey(RecipientId recipient) {
80 var targetProfile = profileProvider.getProfile(recipient);
81 if (targetProfile == null) {
82 return null;
83 }
84
85 switch (targetProfile.getUnidentifiedAccessMode()) {
86 case ENABLED:
87 var theirProfileKey = account.getProfileStore().getProfileKey(recipient);
88 if (theirProfileKey == null) {
89 return null;
90 }
91
92 return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
93 case UNRESTRICTED:
94 return createUnrestrictedUnidentifiedAccess();
95 default:
96 return null;
97 }
98 }
99
100 public Optional<UnidentifiedAccessPair> getAccessForSync() {
101 var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
102 var selfUnidentifiedAccessCertificate = getSenderCertificate();
103
104 if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) {
105 return Optional.absent();
106 }
107
108 try {
109 return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
110 selfUnidentifiedAccessCertificate),
111 new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
112 } catch (InvalidCertificateException e) {
113 return Optional.absent();
114 }
115 }
116
117 public List<Optional<UnidentifiedAccessPair>> getAccessFor(List<RecipientId> recipients) {
118 return recipients.stream().map(this::getAccessFor).collect(Collectors.toList());
119 }
120
121 public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipient) {
122 var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
123 var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
124 var selfUnidentifiedAccessCertificate = getSenderCertificateFor(recipient);
125
126 if (recipientUnidentifiedAccessKey == null
127 || selfUnidentifiedAccessKey == null
128 || selfUnidentifiedAccessCertificate == null) {
129 return Optional.absent();
130 }
131
132 try {
133 return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
134 selfUnidentifiedAccessCertificate),
135 new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
136 } catch (InvalidCertificateException e) {
137 return Optional.absent();
138 }
139 }
140
141 private static byte[] createUnrestrictedUnidentifiedAccess() {
142 return getSecretBytes(16);
143 }
144 }