]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/helper/UnidentifiedAccessHelper.java
Replace collect(Collectors.toList()) with toList()
[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.signal.libsignal.metadata.certificate.SenderCertificate;
9 import org.slf4j.Logger;
10 import org.slf4j.LoggerFactory;
11 import org.whispersystems.libsignal.util.guava.Optional;
12 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
13 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccessPair;
14
15 import java.io.IOException;
16 import java.util.List;
17 import java.util.concurrent.TimeUnit;
18
19 import static org.whispersystems.signalservice.internal.util.Util.getSecretBytes;
20
21 public class UnidentifiedAccessHelper {
22
23 private final static Logger logger = LoggerFactory.getLogger(UnidentifiedAccessHelper.class);
24 private final static long CERTIFICATE_EXPIRATION_BUFFER = TimeUnit.DAYS.toMillis(1);
25
26 private final SignalAccount account;
27 private final SignalDependencies dependencies;
28 private final SelfProfileKeyProvider selfProfileKeyProvider;
29 private final ProfileProvider profileProvider;
30
31 private SenderCertificate privacySenderCertificate;
32 private SenderCertificate senderCertificate;
33
34 public UnidentifiedAccessHelper(
35 final SignalAccount account,
36 final SignalDependencies dependencies,
37 final SelfProfileKeyProvider selfProfileKeyProvider,
38 final ProfileProvider profileProvider
39 ) {
40 this.account = account;
41 this.dependencies = dependencies;
42 this.selfProfileKeyProvider = selfProfileKeyProvider;
43 this.profileProvider = profileProvider;
44 }
45
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
51 )) {
52 logger.debug("Using normal sender certificate for message to {}", recipientId);
53 return getSenderCertificate();
54 } else {
55 logger.debug("Using phone number privacy sender certificate for message to {}", recipientId);
56 return getSenderCertificateForPhoneNumberPrivacy();
57 }
58 }
59
60 private byte[] getSenderCertificateForPhoneNumberPrivacy() {
61 if (privacySenderCertificate != null && System.currentTimeMillis() < (
62 privacySenderCertificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER
63 )) {
64 return privacySenderCertificate.getSerialized();
65 }
66 try {
67 final var certificate = dependencies.getAccountManager().getSenderCertificateForPhoneNumberPrivacy();
68 privacySenderCertificate = new SenderCertificate(certificate);
69 return certificate;
70 } catch (IOException | InvalidCertificateException e) {
71 logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
72 return null;
73 }
74 }
75
76 private byte[] getSenderCertificate() {
77 if (senderCertificate != null && System.currentTimeMillis() < (
78 senderCertificate.getExpiration() - CERTIFICATE_EXPIRATION_BUFFER
79 )) {
80 return senderCertificate.getSerialized();
81 }
82 try {
83 final var certificate = dependencies.getAccountManager().getSenderCertificate();
84 this.senderCertificate = new SenderCertificate(certificate);
85 return certificate;
86 } catch (IOException | InvalidCertificateException e) {
87 logger.warn("Failed to get sender certificate, ignoring: {}", e.getMessage());
88 return null;
89 }
90 }
91
92 private byte[] getSelfUnidentifiedAccessKey() {
93 return UnidentifiedAccess.deriveAccessKeyFrom(selfProfileKeyProvider.getProfileKey());
94 }
95
96 public byte[] getTargetUnidentifiedAccessKey(RecipientId recipient) {
97 var targetProfile = profileProvider.getProfile(recipient);
98 if (targetProfile == null) {
99 return null;
100 }
101
102 switch (targetProfile.getUnidentifiedAccessMode()) {
103 case ENABLED:
104 var theirProfileKey = account.getProfileStore().getProfileKey(recipient);
105 if (theirProfileKey == null) {
106 return null;
107 }
108
109 return UnidentifiedAccess.deriveAccessKeyFrom(theirProfileKey);
110 case UNRESTRICTED:
111 return createUnrestrictedUnidentifiedAccess();
112 default:
113 return null;
114 }
115 }
116
117 public Optional<UnidentifiedAccessPair> getAccessForSync() {
118 var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
119 var selfUnidentifiedAccessCertificate = getSenderCertificate();
120
121 if (selfUnidentifiedAccessKey == null || selfUnidentifiedAccessCertificate == null) {
122 return Optional.absent();
123 }
124
125 try {
126 return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(selfUnidentifiedAccessKey,
127 selfUnidentifiedAccessCertificate),
128 new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
129 } catch (InvalidCertificateException e) {
130 return Optional.absent();
131 }
132 }
133
134 public List<Optional<UnidentifiedAccessPair>> getAccessFor(List<RecipientId> recipients) {
135 return recipients.stream().map(this::getAccessFor).toList();
136 }
137
138 public Optional<UnidentifiedAccessPair> getAccessFor(RecipientId recipient) {
139 var recipientUnidentifiedAccessKey = getTargetUnidentifiedAccessKey(recipient);
140 var selfUnidentifiedAccessKey = getSelfUnidentifiedAccessKey();
141 var selfUnidentifiedAccessCertificate = getSenderCertificateFor(recipient);
142
143 if (recipientUnidentifiedAccessKey == null
144 || selfUnidentifiedAccessKey == null
145 || selfUnidentifiedAccessCertificate == null) {
146 return Optional.absent();
147 }
148
149 try {
150 return Optional.of(new UnidentifiedAccessPair(new UnidentifiedAccess(recipientUnidentifiedAccessKey,
151 selfUnidentifiedAccessCertificate),
152 new UnidentifiedAccess(selfUnidentifiedAccessKey, selfUnidentifiedAccessCertificate)));
153 } catch (InvalidCertificateException e) {
154 return Optional.absent();
155 }
156 }
157
158 private static byte[] createUnrestrictedUnidentifiedAccess() {
159 return getSecretBytes(16);
160 }
161 }