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