That fuck shit the fascists are using
1package org.tm.archive.util;
2
3import android.content.Context;
4
5import androidx.annotation.NonNull;
6import androidx.annotation.Nullable;
7import androidx.annotation.StringRes;
8
9import org.signal.core.util.concurrent.ListenableFuture;
10import org.signal.core.util.concurrent.SettableFuture;
11import org.signal.core.util.concurrent.SignalExecutors;
12import org.signal.core.util.concurrent.SimpleTask;
13import org.signal.core.util.logging.Log;
14import org.signal.libsignal.protocol.IdentityKey;
15import org.signal.libsignal.protocol.InvalidKeyException;
16import org.signal.libsignal.protocol.SignalProtocolAddress;
17import org.signal.libsignal.protocol.state.SessionRecord;
18import org.signal.libsignal.protocol.state.SessionStore;
19import org.tm.archive.R;
20import org.tm.archive.crypto.ReentrantSessionLock;
21import org.tm.archive.crypto.storage.SignalIdentityKeyStore;
22import org.tm.archive.database.GroupTable;
23import org.tm.archive.database.IdentityTable;
24import org.tm.archive.database.MessageTable;
25import org.tm.archive.database.MessageTable.InsertResult;
26import org.tm.archive.database.SignalDatabase;
27import org.tm.archive.database.model.GroupRecord;
28import org.tm.archive.database.model.IdentityRecord;
29import org.tm.archive.dependencies.ApplicationDependencies;
30import org.tm.archive.mms.IncomingMessage;
31import org.tm.archive.mms.MmsException;
32import org.tm.archive.mms.OutgoingMessage;
33import org.tm.archive.notifications.v2.ConversationId;
34import org.tm.archive.recipients.Recipient;
35import org.tm.archive.recipients.RecipientId;
36import org.whispersystems.signalservice.api.SignalSessionLock;
37import org.whispersystems.signalservice.api.messages.multidevice.VerifiedMessage;
38import org.whispersystems.signalservice.api.push.ServiceId;
39import org.whispersystems.signalservice.api.push.SignalServiceAddress;
40import org.whispersystems.signalservice.internal.push.Verified;
41
42import java.util.List;
43import java.util.Optional;
44
45public final class IdentityUtil {
46
47 private IdentityUtil() {}
48
49 private static final String TAG = Log.tag(IdentityUtil.class);
50
51 public static ListenableFuture<Optional<IdentityRecord>> getRemoteIdentityKey(final Context context, final Recipient recipient) {
52 final SettableFuture<Optional<IdentityRecord>> future = new SettableFuture<>();
53 final RecipientId recipientId = recipient.getId();
54
55 SimpleTask.run(SignalExecutors.BOUNDED,
56 () -> ApplicationDependencies.getProtocolStore().aci().identities().getIdentityRecord(recipientId),
57 future::set);
58
59 return future;
60 }
61
62 public static void markIdentityVerified(Context context, Recipient recipient, boolean verified, boolean remote)
63 {
64 long time = System.currentTimeMillis();
65 MessageTable smsDatabase = SignalDatabase.messages();
66 GroupTable groupDatabase = SignalDatabase.groups();
67
68 try (GroupTable.Reader reader = groupDatabase.getGroups()) {
69
70 GroupRecord groupRecord;
71
72 while ((groupRecord = reader.getNext()) != null) {
73 if (groupRecord.getMembers().contains(recipient.getId()) && groupRecord.isActive() && !groupRecord.isMms()) {
74
75 if (remote) {
76 IncomingMessage incoming = verified ? IncomingMessage.identityVerified(recipient.getId(), time, groupRecord.getId())
77 : IncomingMessage.identityDefault(recipient.getId(), time, groupRecord.getId());
78
79 try {
80 smsDatabase.insertMessageInbox(incoming);
81 } catch (MmsException e) {
82 throw new AssertionError(e);
83 }
84 } else {
85 RecipientId recipientId = SignalDatabase.recipients().getOrInsertFromGroupId(groupRecord.getId());
86 Recipient groupRecipient = Recipient.resolved(recipientId);
87 long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(groupRecipient);
88
89 OutgoingMessage outgoing;
90 if (verified) {
91 outgoing = OutgoingMessage.identityVerifiedMessage(recipient, time);
92 } else {
93 outgoing = OutgoingMessage.identityDefaultMessage(recipient, time);
94 }
95
96 try {
97 SignalDatabase.messages().insertMessageOutbox(outgoing, threadId, false, null);
98 } catch (MmsException e) {
99 throw new AssertionError(e);
100 }
101 SignalDatabase.threads().update(threadId, true);
102 }
103 }
104 }
105 }
106
107 if (remote) {
108 IncomingMessage incoming = verified ? IncomingMessage.identityVerified(recipient.getId(), time, null)
109 : IncomingMessage.identityDefault(recipient.getId(), time, null);
110
111 try {
112 smsDatabase.insertMessageInbox(incoming);
113 } catch (MmsException e) {
114 throw new AssertionError(e);
115 }
116 } else {
117 OutgoingMessage outgoing;
118 if (verified) {
119 outgoing = OutgoingMessage.identityVerifiedMessage(recipient, time);
120 } else {
121 outgoing = OutgoingMessage.identityDefaultMessage(recipient, time);
122 }
123
124 long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient);
125
126 Log.i(TAG, "Inserting verified outbox...");
127 try {
128 SignalDatabase.messages().insertMessageOutbox(outgoing, threadId, false, null);
129 } catch (MmsException e) {
130 throw new AssertionError();
131 }
132 SignalDatabase.threads().update(threadId, true);
133 }
134 }
135
136 public static void markIdentityUpdate(@NonNull Context context, @NonNull RecipientId recipientId) {
137 Log.w(TAG, "Inserting safety number change event(s) for " + recipientId, new Throwable());
138
139 long time = System.currentTimeMillis();
140 MessageTable smsDatabase = SignalDatabase.messages();
141 GroupTable groupDatabase = SignalDatabase.groups();
142
143 try (GroupTable.Reader reader = groupDatabase.getGroups()) {
144 GroupRecord groupRecord;
145
146 while ((groupRecord = reader.getNext()) != null) {
147 if (groupRecord.getMembers().contains(recipientId) && groupRecord.isActive()) {
148 IncomingMessage groupUpdate = IncomingMessage.identityUpdate(recipientId, time, groupRecord.getId());
149 smsDatabase.insertMessageInbox(groupUpdate);
150 }
151 }
152 } catch (MmsException e) {
153 throw new AssertionError(e);
154 }
155
156 try {
157 IncomingMessage individualUpdate = IncomingMessage.identityUpdate(recipientId, time, null);
158 Optional<InsertResult> insertResult = smsDatabase.insertMessageInbox(individualUpdate);
159
160 if (insertResult.isPresent()) {
161 ApplicationDependencies.getMessageNotifier().updateNotification(context, ConversationId.forConversation(insertResult.get().getThreadId()));
162 }
163 } catch (MmsException e) {
164 throw new AssertionError(e);
165 }
166
167 SignalDatabase.messageLog().deleteAllForRecipient(recipientId);
168 }
169
170 public static void saveIdentity(String user, IdentityKey identityKey) {
171 try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
172 SessionStore sessionStore = ApplicationDependencies.getProtocolStore().aci();
173 SignalProtocolAddress address = new SignalProtocolAddress(user, SignalServiceAddress.DEFAULT_DEVICE_ID);
174
175 if (ApplicationDependencies.getProtocolStore().aci().identities().saveIdentity(address, identityKey)) {
176 if (sessionStore.containsSession(address)) {
177 SessionRecord sessionRecord = sessionStore.loadSession(address);
178 sessionRecord.archiveCurrentState();
179
180 sessionStore.storeSession(address, sessionRecord);
181 }
182 }
183 }
184 }
185
186 public static void processVerifiedMessage(Context context, Verified verified) throws InvalidKeyException {
187 SignalServiceAddress destination = new SignalServiceAddress(ServiceId.parseOrThrow(verified.destinationAci));
188 IdentityKey identityKey = new IdentityKey(verified.identityKey.toByteArray(), 0);
189 VerifiedMessage.VerifiedState state;
190
191 switch (verified.state) {
192 case DEFAULT:
193 state = VerifiedMessage.VerifiedState.DEFAULT;
194 break;
195 case VERIFIED:
196 state = VerifiedMessage.VerifiedState.VERIFIED;
197 break;
198 case UNVERIFIED:
199 state = VerifiedMessage.VerifiedState.UNVERIFIED;
200 break;
201 default:
202 throw new IllegalArgumentException();
203 }
204
205 processVerifiedMessage(context, new VerifiedMessage(destination, identityKey, state, System.currentTimeMillis()));
206 }
207
208 public static void processVerifiedMessage(Context context, VerifiedMessage verifiedMessage) {
209 try(SignalSessionLock.Lock unused = ReentrantSessionLock.INSTANCE.acquire()) {
210 SignalIdentityKeyStore identityStore = ApplicationDependencies.getProtocolStore().aci().identities();
211 Recipient recipient = Recipient.externalPush(verifiedMessage.getDestination());
212
213 if (recipient.isSelf()) {
214 Log.w(TAG, "Attempting to change verified status of self to " + verifiedMessage.getVerified() + ", skipping.");
215 return;
216 }
217
218 Optional<IdentityRecord> identityRecord = identityStore.getIdentityRecord(recipient.getId());
219
220 if (!identityRecord.isPresent() && verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT) {
221 Log.w(TAG, "No existing record for default status");
222 return;
223 }
224
225 if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.DEFAULT &&
226 identityRecord.isPresent() &&
227 identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey()) &&
228 identityRecord.get().getVerifiedStatus() != IdentityTable.VerifiedStatus.DEFAULT)
229 {
230 Log.i(TAG, "Setting " + recipient.getId() + " verified status to " + IdentityTable.VerifiedStatus.DEFAULT);
231 identityStore.setVerified(recipient.getId(), identityRecord.get().getIdentityKey(), IdentityTable.VerifiedStatus.DEFAULT);
232 markIdentityVerified(context, recipient, false, true);
233 }
234
235 if (verifiedMessage.getVerified() == VerifiedMessage.VerifiedState.VERIFIED &&
236 (!identityRecord.isPresent() ||
237 (identityRecord.isPresent() && !identityRecord.get().getIdentityKey().equals(verifiedMessage.getIdentityKey())) ||
238 (identityRecord.isPresent() && identityRecord.get().getVerifiedStatus() != IdentityTable.VerifiedStatus.VERIFIED)))
239 {
240 Log.i(TAG, "Setting " + recipient.getId() + " verified status to " + IdentityTable.VerifiedStatus.VERIFIED);
241 saveIdentity(verifiedMessage.getDestination().getIdentifier(), verifiedMessage.getIdentityKey());
242 identityStore.setVerified(recipient.getId(), verifiedMessage.getIdentityKey(), IdentityTable.VerifiedStatus.VERIFIED);
243 markIdentityVerified(context, recipient, true, true);
244 }
245 }
246 }
247
248
249 public static @Nullable String getUnverifiedBannerDescription(@NonNull Context context,
250 @NonNull List<Recipient> unverified)
251 {
252 return getPluralizedIdentityDescription(context, unverified,
253 R.string.IdentityUtil_unverified_banner_one,
254 R.string.IdentityUtil_unverified_banner_two,
255 R.string.IdentityUtil_unverified_banner_many);
256 }
257
258 public static @Nullable String getUnverifiedSendDialogDescription(@NonNull Context context,
259 @NonNull List<Recipient> unverified)
260 {
261 return getPluralizedIdentityDescription(context, unverified,
262 R.string.IdentityUtil_unverified_dialog_one,
263 R.string.IdentityUtil_unverified_dialog_two,
264 R.string.IdentityUtil_unverified_dialog_many);
265 }
266
267 public static @Nullable String getUntrustedSendDialogDescription(@NonNull Context context,
268 @NonNull List<Recipient> untrusted)
269 {
270 return getPluralizedIdentityDescription(context, untrusted,
271 R.string.IdentityUtil_untrusted_dialog_one,
272 R.string.IdentityUtil_untrusted_dialog_two,
273 R.string.IdentityUtil_untrusted_dialog_many);
274 }
275
276 private static @Nullable String getPluralizedIdentityDescription(@NonNull Context context,
277 @NonNull List<Recipient> recipients,
278 @StringRes int resourceOne,
279 @StringRes int resourceTwo,
280 @StringRes int resourceMany)
281 {
282 if (recipients.isEmpty()) return null;
283
284 if (recipients.size() == 1) {
285 String name = recipients.get(0).getDisplayName(context);
286 return context.getString(resourceOne, name);
287 } else {
288 String firstName = recipients.get(0).getDisplayName(context);
289 String secondName = recipients.get(1).getDisplayName(context);
290
291 if (recipients.size() == 2) {
292 return context.getString(resourceTwo, firstName, secondName);
293 } else {
294 int othersCount = recipients.size() - 2;
295 String nMore = context.getResources().getQuantityString(R.plurals.identity_others, othersCount, othersCount);
296
297 return context.getString(resourceMany, firstName, secondName, nMore);
298 }
299 }
300 }
301}