]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
8d2fbd0f40a40f6fda203335c39e7a3562ecd54c
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / storage / SignalAccount.java
1 package org.asamk.signal.manager.storage;
2
3 import com.fasterxml.jackson.databind.JsonNode;
4 import com.fasterxml.jackson.databind.ObjectMapper;
5
6 import org.asamk.signal.manager.Settings;
7 import org.asamk.signal.manager.api.Contact;
8 import org.asamk.signal.manager.api.GroupId;
9 import org.asamk.signal.manager.api.Pair;
10 import org.asamk.signal.manager.api.Profile;
11 import org.asamk.signal.manager.api.ServiceEnvironment;
12 import org.asamk.signal.manager.api.TrustLevel;
13 import org.asamk.signal.manager.helper.RecipientAddressResolver;
14 import org.asamk.signal.manager.storage.configuration.ConfigurationStore;
15 import org.asamk.signal.manager.storage.configuration.LegacyConfigurationStore;
16 import org.asamk.signal.manager.storage.contacts.ContactsStore;
17 import org.asamk.signal.manager.storage.contacts.LegacyJsonContactsStore;
18 import org.asamk.signal.manager.storage.groups.GroupInfoV1;
19 import org.asamk.signal.manager.storage.groups.GroupStore;
20 import org.asamk.signal.manager.storage.groups.LegacyGroupStore;
21 import org.asamk.signal.manager.storage.identities.IdentityKeyStore;
22 import org.asamk.signal.manager.storage.identities.LegacyIdentityKeyStore;
23 import org.asamk.signal.manager.storage.identities.SignalIdentityKeyStore;
24 import org.asamk.signal.manager.storage.keyValue.KeyValueEntry;
25 import org.asamk.signal.manager.storage.keyValue.KeyValueStore;
26 import org.asamk.signal.manager.storage.messageCache.MessageCache;
27 import org.asamk.signal.manager.storage.prekeys.KyberPreKeyStore;
28 import org.asamk.signal.manager.storage.prekeys.LegacyPreKeyStore;
29 import org.asamk.signal.manager.storage.prekeys.LegacySignedPreKeyStore;
30 import org.asamk.signal.manager.storage.prekeys.PreKeyStore;
31 import org.asamk.signal.manager.storage.prekeys.SignedPreKeyStore;
32 import org.asamk.signal.manager.storage.profiles.LegacyProfileStore;
33 import org.asamk.signal.manager.storage.profiles.ProfileStore;
34 import org.asamk.signal.manager.storage.protocol.LegacyJsonSignalProtocolStore;
35 import org.asamk.signal.manager.storage.protocol.SignalProtocolStore;
36 import org.asamk.signal.manager.storage.recipients.CdsiStore;
37 import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore;
38 import org.asamk.signal.manager.storage.recipients.LegacyRecipientStore2;
39 import org.asamk.signal.manager.storage.recipients.RecipientAddress;
40 import org.asamk.signal.manager.storage.recipients.RecipientId;
41 import org.asamk.signal.manager.storage.recipients.RecipientIdCreator;
42 import org.asamk.signal.manager.storage.recipients.RecipientResolver;
43 import org.asamk.signal.manager.storage.recipients.RecipientStore;
44 import org.asamk.signal.manager.storage.recipients.RecipientTrustedResolver;
45 import org.asamk.signal.manager.storage.sendLog.MessageSendLogStore;
46 import org.asamk.signal.manager.storage.senderKeys.LegacySenderKeyRecordStore;
47 import org.asamk.signal.manager.storage.senderKeys.LegacySenderKeySharedStore;
48 import org.asamk.signal.manager.storage.senderKeys.SenderKeyStore;
49 import org.asamk.signal.manager.storage.sessions.LegacySessionStore;
50 import org.asamk.signal.manager.storage.sessions.SessionStore;
51 import org.asamk.signal.manager.storage.stickers.LegacyStickerStore;
52 import org.asamk.signal.manager.storage.stickers.StickerStore;
53 import org.asamk.signal.manager.storage.threads.LegacyJsonThreadStore;
54 import org.asamk.signal.manager.util.IOUtils;
55 import org.asamk.signal.manager.util.KeyUtils;
56 import org.signal.libsignal.protocol.IdentityKeyPair;
57 import org.signal.libsignal.protocol.InvalidMessageException;
58 import org.signal.libsignal.protocol.SignalProtocolAddress;
59 import org.signal.libsignal.protocol.state.KyberPreKeyRecord;
60 import org.signal.libsignal.protocol.state.PreKeyRecord;
61 import org.signal.libsignal.protocol.state.SessionRecord;
62 import org.signal.libsignal.protocol.state.SignedPreKeyRecord;
63 import org.signal.libsignal.protocol.util.KeyHelper;
64 import org.signal.libsignal.zkgroup.InvalidInputException;
65 import org.signal.libsignal.zkgroup.profiles.ProfileKey;
66 import org.slf4j.Logger;
67 import org.slf4j.LoggerFactory;
68 import org.whispersystems.signalservice.api.AccountEntropyPool;
69 import org.whispersystems.signalservice.api.SignalServiceAccountDataStore;
70 import org.whispersystems.signalservice.api.SignalServiceDataStore;
71 import org.whispersystems.signalservice.api.account.AccountAttributes;
72 import org.whispersystems.signalservice.api.account.PreKeyCollection;
73 import org.whispersystems.signalservice.api.backup.MediaRootBackupKey;
74 import org.whispersystems.signalservice.api.crypto.UnidentifiedAccess;
75 import org.whispersystems.signalservice.api.kbs.MasterKey;
76 import org.whispersystems.signalservice.api.push.ServiceId;
77 import org.whispersystems.signalservice.api.push.ServiceId.ACI;
78 import org.whispersystems.signalservice.api.push.ServiceId.PNI;
79 import org.whispersystems.signalservice.api.push.ServiceIdType;
80 import org.whispersystems.signalservice.api.push.SignalServiceAddress;
81 import org.whispersystems.signalservice.api.push.UsernameLinkComponents;
82 import org.whispersystems.signalservice.api.storage.SignalStorageManifest;
83 import org.whispersystems.signalservice.api.storage.StorageKey;
84 import org.whispersystems.signalservice.api.util.CredentialsProvider;
85 import org.whispersystems.signalservice.api.util.UuidUtil;
86
87 import java.io.ByteArrayInputStream;
88 import java.io.ByteArrayOutputStream;
89 import java.io.Closeable;
90 import java.io.File;
91 import java.io.FileInputStream;
92 import java.io.FileOutputStream;
93 import java.io.IOException;
94 import java.io.RandomAccessFile;
95 import java.nio.channels.Channels;
96 import java.nio.channels.ClosedChannelException;
97 import java.nio.channels.FileChannel;
98 import java.nio.channels.FileLock;
99 import java.nio.file.Files;
100 import java.sql.Connection;
101 import java.sql.SQLException;
102 import java.util.Base64;
103 import java.util.Comparator;
104 import java.util.HashSet;
105 import java.util.List;
106 import java.util.Optional;
107 import java.util.UUID;
108 import java.util.function.Function;
109 import java.util.function.Supplier;
110
111 import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_MAXIMUM_ID;
112 import static org.asamk.signal.manager.config.ServiceConfig.getCapabilities;
113
114 public class SignalAccount implements Closeable {
115
116 private static final Logger logger = LoggerFactory.getLogger(SignalAccount.class);
117
118 private static final int MINIMUM_STORAGE_VERSION = 1;
119 private static final int CURRENT_STORAGE_VERSION = 9;
120
121 private final Object LOCK = new Object();
122
123 private final ObjectMapper jsonProcessor = Utils.createStorageObjectMapper();
124
125 private final FileChannel fileChannel;
126 private final FileLock lock;
127
128 private int previousStorageVersion;
129
130 private File dataPath;
131 private String accountPath;
132
133 private ServiceEnvironment serviceEnvironment;
134 private String number;
135 private String username;
136 private UsernameLinkComponents usernameLink;
137 private String encryptedDeviceName;
138 private int deviceId = 0;
139 private String password;
140 private String registrationLockPin;
141 private MasterKey pinMasterKey;
142 private StorageKey storageKey;
143 private AccountEntropyPool accountEntropyPool;
144 private MediaRootBackupKey mediaRootBackupKey;
145 private ProfileKey profileKey;
146
147 private Settings settings;
148
149 private final KeyValueEntry<String> verificationSessionId = new KeyValueEntry<>("verification-session-id",
150 String.class);
151 private final KeyValueEntry<String> verificationSessionNumber = new KeyValueEntry<>("verification-session-number",
152 String.class);
153 private final KeyValueEntry<Long> lastReceiveTimestamp = new KeyValueEntry<>("last-receive-timestamp",
154 long.class,
155 0L);
156 private final KeyValueEntry<Boolean> needsToRetryFailedMessages = new KeyValueEntry<>("retry-failed-messages",
157 Boolean.class,
158 true);
159 private final KeyValueEntry<byte[]> cdsiToken = new KeyValueEntry<>("cdsi-token", byte[].class);
160 private final KeyValueEntry<Long> lastRecipientsRefresh = new KeyValueEntry<>("last-recipients-refresh",
161 long.class);
162 private final KeyValueEntry<Long> storageManifestVersion = new KeyValueEntry<>("storage-manifest-version",
163 long.class,
164 -1L);
165 private final KeyValueEntry<Boolean> unrestrictedUnidentifiedAccess = new KeyValueEntry<>(
166 "unrestricted-unidentified-access",
167 Boolean.class,
168 false);
169 private boolean isMultiDevice = false;
170 private boolean registered = false;
171
172 private final AccountData<ACI> aciAccountData = new AccountData<>(ServiceIdType.ACI);
173 private final AccountData<PNI> pniAccountData = new AccountData<>(ServiceIdType.PNI);
174 private IdentityKeyStore identityKeyStore;
175 private SenderKeyStore senderKeyStore;
176 private GroupStore groupStore;
177 private RecipientStore recipientStore;
178 private StickerStore stickerStore;
179 private UnknownStorageIdStore unknownStorageIdStore;
180 private ConfigurationStore configurationStore;
181 private KeyValueStore keyValueStore;
182 private CdsiStore cdsiStore;
183
184 private MessageCache messageCache;
185 private MessageSendLogStore messageSendLogStore;
186
187 private AccountDatabase accountDatabase;
188 private RecipientId selfRecipientId;
189
190 private SignalAccount(final FileChannel fileChannel, final FileLock lock) {
191 this.fileChannel = fileChannel;
192 this.lock = lock;
193 }
194
195 public static SignalAccount load(
196 File dataPath,
197 String accountPath,
198 boolean waitForLock,
199 final Settings settings
200 ) throws IOException {
201 logger.trace("Opening account file");
202 final var fileName = getFileName(dataPath, accountPath);
203 final var pair = openFileChannel(fileName, waitForLock);
204 try {
205 var signalAccount = new SignalAccount(pair.first(), pair.second());
206 signalAccount.load(dataPath, accountPath, settings);
207 signalAccount.migrateLegacyConfigs();
208 signalAccount.init();
209
210 return signalAccount;
211 } catch (Throwable e) {
212 pair.second().close();
213 pair.first().close();
214 throw e;
215 }
216 }
217
218 public static SignalAccount create(
219 File dataPath,
220 String accountPath,
221 String number,
222 ServiceEnvironment serviceEnvironment,
223 IdentityKeyPair aciIdentityKey,
224 IdentityKeyPair pniIdentityKey,
225 ProfileKey profileKey,
226 final Settings settings
227 ) throws IOException {
228 IOUtils.createPrivateDirectories(dataPath);
229 var fileName = getFileName(dataPath, accountPath);
230 if (!fileName.exists()) {
231 IOUtils.createPrivateFile(fileName);
232 }
233
234 final var pair = openFileChannel(fileName, true);
235 var signalAccount = new SignalAccount(pair.first(), pair.second());
236
237 signalAccount.accountPath = accountPath;
238 signalAccount.number = number;
239 signalAccount.serviceEnvironment = serviceEnvironment;
240 signalAccount.profileKey = profileKey;
241 signalAccount.password = KeyUtils.createPassword();
242 signalAccount.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
243
244 signalAccount.dataPath = dataPath;
245 signalAccount.aciAccountData.setIdentityKeyPair(aciIdentityKey);
246 signalAccount.pniAccountData.setIdentityKeyPair(pniIdentityKey);
247 signalAccount.aciAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
248 signalAccount.pniAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
249 signalAccount.initAllPreKeyIds();
250 signalAccount.settings = settings;
251
252 signalAccount.registered = false;
253
254 signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
255 signalAccount.init();
256 signalAccount.save();
257
258 return signalAccount;
259 }
260
261 public static SignalAccount createLinkedAccount(
262 final File dataPath,
263 final String accountPath,
264 final ServiceEnvironment serviceEnvironment,
265 final Settings settings
266 ) throws IOException {
267 IOUtils.createPrivateDirectories(dataPath);
268 var fileName = getFileName(dataPath, accountPath);
269 IOUtils.createPrivateFile(fileName);
270
271 final var pair = openFileChannel(fileName, true);
272 final var signalAccount = new SignalAccount(pair.first(), pair.second());
273
274 signalAccount.dataPath = dataPath;
275 signalAccount.accountPath = accountPath;
276 signalAccount.serviceEnvironment = serviceEnvironment;
277 signalAccount.aciAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
278 signalAccount.pniAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
279 signalAccount.settings = settings;
280
281 signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
282
283 return signalAccount;
284 }
285
286 public void setProvisioningData(
287 final String number,
288 final ACI aci,
289 final PNI pni,
290 final String password,
291 final String encryptedDeviceName,
292 final IdentityKeyPair aciIdentity,
293 final IdentityKeyPair pniIdentity,
294 final ProfileKey profileKey,
295 final MasterKey masterKey,
296 final AccountEntropyPool accountEntropyPool,
297 final MediaRootBackupKey mediaRootBackupKey
298 ) {
299 this.deviceId = 0;
300 this.number = number;
301 this.aciAccountData.setServiceId(aci);
302 this.pniAccountData.setServiceId(pni);
303 this.init();
304 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
305 this.password = password;
306 this.profileKey = profileKey;
307 this.encryptedDeviceName = encryptedDeviceName;
308 this.aciAccountData.setIdentityKeyPair(aciIdentity);
309 this.pniAccountData.setIdentityKeyPair(pniIdentity);
310 this.registered = false;
311 this.isMultiDevice = true;
312 setLastReceiveTimestamp(0L);
313 if (accountEntropyPool != null) {
314 this.pinMasterKey = null;
315 this.accountEntropyPool = accountEntropyPool;
316 } else {
317 this.pinMasterKey = masterKey;
318 this.accountEntropyPool = null;
319 }
320 this.mediaRootBackupKey = mediaRootBackupKey;
321 getKeyValueStore().storeEntry(storageManifestVersion, -1L);
322 this.setStorageManifest(null);
323 this.storageKey = null;
324 getSenderKeyStore().deleteAll();
325 trustSelfIdentity(ServiceIdType.ACI);
326 trustSelfIdentity(ServiceIdType.PNI);
327 aciAccountData.getSessionStore().archiveAllSessions();
328 pniAccountData.getSessionStore().archiveAllSessions();
329 clearAllPreKeys();
330 getKeyValueStore().storeEntry(lastRecipientsRefresh, null);
331 save();
332 }
333
334 public void finishLinking(
335 final int deviceId,
336 final PreKeyCollection aciPreKeys,
337 final PreKeyCollection pniPreKeys
338 ) {
339 this.registered = true;
340 this.deviceId = deviceId;
341 setPreKeys(ServiceIdType.ACI, aciPreKeys);
342 setPreKeys(ServiceIdType.PNI, pniPreKeys);
343 save();
344 }
345
346 public void finishRegistration(
347 final ACI aci,
348 final PNI pni,
349 final MasterKey masterKey,
350 final String pin,
351 final PreKeyCollection aciPreKeys,
352 final PreKeyCollection pniPreKeys
353 ) {
354 this.pinMasterKey = masterKey;
355 this.accountEntropyPool = null;
356 getKeyValueStore().storeEntry(storageManifestVersion, -1L);
357 this.setStorageManifest(null);
358 this.storageKey = null;
359 this.encryptedDeviceName = null;
360 this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
361 this.isMultiDevice = false;
362 this.registered = true;
363 this.aciAccountData.setServiceId(aci);
364 this.pniAccountData.setServiceId(pni);
365 init();
366 this.registrationLockPin = pin;
367 setLastReceiveTimestamp(0L);
368 save();
369
370 setPreKeys(ServiceIdType.ACI, aciPreKeys);
371 setPreKeys(ServiceIdType.PNI, pniPreKeys);
372 aciAccountData.getSessionStore().archiveAllSessions();
373 pniAccountData.getSessionStore().archiveAllSessions();
374 getSenderKeyStore().deleteAll();
375 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
376 trustSelfIdentity(ServiceIdType.ACI);
377 trustSelfIdentity(ServiceIdType.PNI);
378 getKeyValueStore().storeEntry(lastRecipientsRefresh, null);
379 }
380
381 public void initDatabase() {
382 getAccountDatabase();
383 }
384
385 private void init() {
386 this.selfRecipientId = getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
387 }
388
389 private void migrateLegacyConfigs() {
390 if (isPrimaryDevice() && getPniIdentityKeyPair() == null) {
391 logger.trace("Migrating legacy parts of account file");
392 setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair());
393 }
394 }
395
396 private void mergeRecipients(
397 final Connection connection,
398 RecipientId recipientId,
399 RecipientId toBeMergedRecipientId
400 ) throws SQLException {
401 getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId);
402 getGroupStore().mergeRecipients(connection, recipientId, toBeMergedRecipientId);
403 }
404
405 public void removeRecipient(final RecipientId recipientId) {
406 final var recipientAddress = getRecipientStore().resolveRecipientAddress(recipientId);
407 if (recipientAddress.matches(getSelfRecipientAddress())) {
408 throw new RuntimeException("Can't delete self recipient");
409 }
410 getRecipientStore().deleteRecipientData(recipientId);
411 getMessageCache().deleteMessages(recipientId);
412 if (recipientAddress.aci().isPresent()) {
413 final var serviceId = recipientAddress.aci().get();
414 aciAccountData.getSessionStore().deleteAllSessions(serviceId);
415 pniAccountData.getSessionStore().deleteAllSessions(serviceId);
416 getIdentityKeyStore().deleteIdentity(serviceId);
417 getSenderKeyStore().deleteAll(serviceId);
418 }
419 if (recipientAddress.pni().isPresent()) {
420 final var serviceId = recipientAddress.pni().get();
421 aciAccountData.getSessionStore().deleteAllSessions(serviceId);
422 pniAccountData.getSessionStore().deleteAllSessions(serviceId);
423 getIdentityKeyStore().deleteIdentity(serviceId);
424 getSenderKeyStore().deleteAll(serviceId);
425 }
426 }
427
428 public static File getFileName(File dataPath, String account) {
429 return new File(dataPath, account);
430 }
431
432 private static File getUserPath(final File dataPath, final String account) {
433 final var path = new File(dataPath, account + ".d");
434 try {
435 IOUtils.createPrivateDirectories(path);
436 } catch (IOException e) {
437 throw new AssertionError("Failed to create user path", e);
438 }
439 return path;
440 }
441
442 private static File getMessageCachePath(File dataPath, String account) {
443 return new File(getUserPath(dataPath, account), "msg-cache");
444 }
445
446 private static File getStorageManifestFile(File dataPath, String account) {
447 return new File(getUserPath(dataPath, account), "storage-manifest");
448 }
449
450 private static File getDatabaseFile(File dataPath, String account) {
451 return new File(getUserPath(dataPath, account), "account.db");
452 }
453
454 public static boolean accountFileExists(File dataPath, String account) {
455 if (account == null) {
456 return false;
457 }
458 var f = getFileName(dataPath, account);
459 return f.exists() && !f.isDirectory() && f.length() > 0L;
460 }
461
462 private void load(File dataPath, String accountPath, final Settings settings) throws IOException {
463 logger.trace("Loading account file {}", accountPath);
464 this.dataPath = dataPath;
465 this.accountPath = accountPath;
466 this.settings = settings;
467 final JsonNode rootNode;
468 synchronized (fileChannel) {
469 fileChannel.position(0);
470 rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel));
471 }
472
473 var migratedLegacyConfig = false;
474
475 if (rootNode.hasNonNull("version")) {
476 var accountVersion = rootNode.get("version").asInt(1);
477 if (accountVersion > CURRENT_STORAGE_VERSION) {
478 throw new IOException("Config file was created by a more recent version: " + accountVersion);
479 } else if (accountVersion < MINIMUM_STORAGE_VERSION) {
480 throw new IOException("Config file was created by a no longer supported older version: "
481 + accountVersion);
482 }
483 previousStorageVersion = accountVersion;
484 if (accountVersion < CURRENT_STORAGE_VERSION) {
485 migratedLegacyConfig = true;
486 }
487 }
488
489 if (previousStorageVersion < 8) {
490 final var userPath = getUserPath(dataPath, accountPath);
491 loadLegacyFile(userPath, rootNode);
492 migratedLegacyConfig = true;
493 } else {
494 final var storage = jsonProcessor.convertValue(rootNode, Storage.class);
495 serviceEnvironment = ServiceEnvironment.valueOf(storage.serviceEnvironment);
496 registered = storage.registered;
497 number = storage.number;
498 username = storage.username;
499 if ("".equals(username)) {
500 username = null;
501 }
502 encryptedDeviceName = storage.encryptedDeviceName;
503 deviceId = storage.deviceId;
504 isMultiDevice = storage.isMultiDevice;
505 password = storage.password;
506 setAccountData(aciAccountData, storage.aciAccountData, ACI::parseOrThrow);
507 setAccountData(pniAccountData, storage.pniAccountData, PNI::parseOrThrow);
508 registrationLockPin = storage.registrationLockPin;
509 final var base64 = Base64.getDecoder();
510 if (storage.pinMasterKey != null) {
511 pinMasterKey = new MasterKey(base64.decode(storage.pinMasterKey));
512 }
513 if (storage.storageKey != null) {
514 storageKey = new StorageKey(base64.decode(storage.storageKey));
515 }
516 if (storage.accountEntropyPool != null) {
517 accountEntropyPool = new AccountEntropyPool(storage.accountEntropyPool);
518 }
519 if (storage.mediaRootBackupKey != null) {
520 mediaRootBackupKey = new MediaRootBackupKey(base64.decode(storage.mediaRootBackupKey));
521 }
522 if (storage.profileKey != null) {
523 try {
524 profileKey = new ProfileKey(base64.decode(storage.profileKey));
525 } catch (InvalidInputException e) {
526 throw new IOException(
527 "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes",
528 e);
529 }
530 }
531 if (storage.usernameLinkEntropy != null && storage.usernameLinkServerId != null) {
532 usernameLink = new UsernameLinkComponents(base64.decode(storage.usernameLinkEntropy),
533 UUID.fromString(storage.usernameLinkServerId));
534 }
535 }
536
537 if (migratedLegacyConfig) {
538 save();
539 }
540 }
541
542 private <SERVICE_ID extends ServiceId> void setAccountData(
543 AccountData<SERVICE_ID> accountData,
544 Storage.AccountData storage,
545 Function<String, SERVICE_ID> serviceIdParser
546 ) throws IOException {
547 if (storage.serviceId != null) {
548 try {
549 accountData.setServiceId(serviceIdParser.apply(storage.serviceId));
550 } catch (IllegalArgumentException e) {
551 throw new IOException("Config file contains an invalid serviceId, needs to be a valid UUID", e);
552 }
553 }
554 accountData.setLocalRegistrationId(storage.registrationId);
555 if (storage.identityPrivateKey != null && storage.identityPublicKey != null) {
556 final var base64 = Base64.getDecoder();
557 final var publicKeyBytes = base64.decode(storage.identityPublicKey);
558 final var privateKeyBytes = base64.decode(storage.identityPrivateKey);
559 final var keyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
560 accountData.setIdentityKeyPair(keyPair);
561 }
562 accountData.preKeyMetadata.nextPreKeyId = storage.nextPreKeyId;
563 accountData.preKeyMetadata.nextSignedPreKeyId = storage.nextSignedPreKeyId;
564 accountData.preKeyMetadata.activeSignedPreKeyId = storage.activeSignedPreKeyId;
565 accountData.preKeyMetadata.nextKyberPreKeyId = storage.nextKyberPreKeyId;
566 accountData.preKeyMetadata.activeLastResortKyberPreKeyId = storage.activeLastResortKyberPreKeyId;
567 }
568
569 private void loadLegacyFile(final File userPath, final JsonNode rootNode) throws IOException {
570 number = Utils.getNotNullNode(rootNode, "username").asText();
571 if (rootNode.hasNonNull("password")) {
572 password = rootNode.get("password").asText();
573 }
574 if (password == null) {
575 password = KeyUtils.createPassword();
576 }
577
578 if (rootNode.hasNonNull("serviceEnvironment")) {
579 serviceEnvironment = ServiceEnvironment.valueOf(rootNode.get("serviceEnvironment").asText());
580 }
581 if (serviceEnvironment == null) {
582 serviceEnvironment = ServiceEnvironment.LIVE;
583 }
584 registered = Utils.getNotNullNode(rootNode, "registered").asBoolean();
585 if (rootNode.hasNonNull("usernameIdentifier")) {
586 username = rootNode.get("usernameIdentifier").asText();
587 if ("".equals(username)) {
588 username = null;
589 }
590 }
591 if (rootNode.hasNonNull("uuid")) {
592 try {
593 aciAccountData.setServiceId(ACI.parseOrThrow(rootNode.get("uuid").asText()));
594 } catch (IllegalArgumentException e) {
595 throw new IOException("Config file contains an invalid aci/uuid, needs to be a valid UUID", e);
596 }
597 }
598 if (rootNode.hasNonNull("pni")) {
599 try {
600 pniAccountData.setServiceId(PNI.parseOrThrow(rootNode.get("pni").asText()));
601 } catch (IllegalArgumentException e) {
602 throw new IOException("Config file contains an invalid pni, needs to be a valid UUID", e);
603 }
604 }
605 if (rootNode.hasNonNull("sessionId")) {
606 getKeyValueStore().storeEntry(verificationSessionId, rootNode.get("sessionId").asText());
607 }
608 if (rootNode.hasNonNull("sessionNumber")) {
609 getKeyValueStore().storeEntry(verificationSessionNumber, rootNode.get("sessionNumber").asText());
610 }
611 if (rootNode.hasNonNull("deviceName")) {
612 encryptedDeviceName = rootNode.get("deviceName").asText();
613 }
614 if (rootNode.hasNonNull("deviceId")) {
615 deviceId = rootNode.get("deviceId").asInt();
616 }
617 if (rootNode.hasNonNull("isMultiDevice")) {
618 isMultiDevice = rootNode.get("isMultiDevice").asBoolean();
619 }
620 if (rootNode.hasNonNull("lastReceiveTimestamp")) {
621 setLastReceiveTimestamp(rootNode.get("lastReceiveTimestamp").asLong());
622 }
623 int registrationId = 0;
624 if (rootNode.hasNonNull("registrationId")) {
625 registrationId = rootNode.get("registrationId").asInt();
626 }
627 if (rootNode.hasNonNull("pniRegistrationId")) {
628 pniAccountData.setLocalRegistrationId(rootNode.get("pniRegistrationId").asInt());
629 } else {
630 pniAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
631 }
632 IdentityKeyPair aciIdentityKeyPair = null;
633 if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) {
634 final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText());
635 final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("identityPrivateKey").asText());
636 aciIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
637 }
638 if (rootNode.hasNonNull("pniIdentityPrivateKey") && rootNode.hasNonNull("pniIdentityKey")) {
639 final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityKey").asText());
640 final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityPrivateKey").asText());
641 pniAccountData.setIdentityKeyPair(KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes));
642 }
643
644 if (rootNode.hasNonNull("registrationLockPin")) {
645 registrationLockPin = rootNode.get("registrationLockPin").asText();
646 }
647 if (rootNode.hasNonNull("pinMasterKey")) {
648 pinMasterKey = new MasterKey(Base64.getDecoder().decode(rootNode.get("pinMasterKey").asText()));
649 }
650 if (rootNode.hasNonNull("storageKey")) {
651 storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText()));
652 }
653 if (rootNode.hasNonNull("storageManifestVersion")) {
654 getKeyValueStore().storeEntry(storageManifestVersion, rootNode.get("storageManifestVersion").asLong());
655 }
656 if (rootNode.hasNonNull("preKeyIdOffset")) {
657 aciAccountData.preKeyMetadata.nextPreKeyId = rootNode.get("preKeyIdOffset").asInt(1);
658 } else {
659 aciAccountData.preKeyMetadata.nextPreKeyId = getRandomPreKeyIdOffset();
660 }
661 if (rootNode.hasNonNull("nextSignedPreKeyId")) {
662 aciAccountData.preKeyMetadata.nextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(1);
663 } else {
664 aciAccountData.preKeyMetadata.nextSignedPreKeyId = getRandomPreKeyIdOffset();
665 }
666 if (rootNode.hasNonNull("activeSignedPreKeyId")) {
667 aciAccountData.preKeyMetadata.activeSignedPreKeyId = rootNode.get("activeSignedPreKeyId").asInt(-1);
668 } else {
669 aciAccountData.preKeyMetadata.activeSignedPreKeyId = -1;
670 }
671 if (rootNode.hasNonNull("pniPreKeyIdOffset")) {
672 pniAccountData.preKeyMetadata.nextPreKeyId = rootNode.get("pniPreKeyIdOffset").asInt(1);
673 } else {
674 pniAccountData.preKeyMetadata.nextPreKeyId = getRandomPreKeyIdOffset();
675 }
676 if (rootNode.hasNonNull("pniNextSignedPreKeyId")) {
677 pniAccountData.preKeyMetadata.nextSignedPreKeyId = rootNode.get("pniNextSignedPreKeyId").asInt(1);
678 } else {
679 pniAccountData.preKeyMetadata.nextSignedPreKeyId = getRandomPreKeyIdOffset();
680 }
681 if (rootNode.hasNonNull("pniActiveSignedPreKeyId")) {
682 pniAccountData.preKeyMetadata.activeSignedPreKeyId = rootNode.get("pniActiveSignedPreKeyId").asInt(-1);
683 } else {
684 pniAccountData.preKeyMetadata.activeSignedPreKeyId = -1;
685 }
686 if (rootNode.hasNonNull("kyberPreKeyIdOffset")) {
687 aciAccountData.preKeyMetadata.nextKyberPreKeyId = rootNode.get("kyberPreKeyIdOffset").asInt(1);
688 } else {
689 aciAccountData.preKeyMetadata.nextKyberPreKeyId = getRandomPreKeyIdOffset();
690 }
691 if (rootNode.hasNonNull("activeLastResortKyberPreKeyId")) {
692 aciAccountData.preKeyMetadata.activeLastResortKyberPreKeyId = rootNode.get("activeLastResortKyberPreKeyId")
693 .asInt(-1);
694 } else {
695 aciAccountData.preKeyMetadata.activeLastResortKyberPreKeyId = -1;
696 }
697 if (rootNode.hasNonNull("pniKyberPreKeyIdOffset")) {
698 pniAccountData.preKeyMetadata.nextKyberPreKeyId = rootNode.get("pniKyberPreKeyIdOffset").asInt(1);
699 } else {
700 pniAccountData.preKeyMetadata.nextKyberPreKeyId = getRandomPreKeyIdOffset();
701 }
702 if (rootNode.hasNonNull("pniActiveLastResortKyberPreKeyId")) {
703 pniAccountData.preKeyMetadata.activeLastResortKyberPreKeyId = rootNode.get(
704 "pniActiveLastResortKyberPreKeyId").asInt(-1);
705 } else {
706 pniAccountData.preKeyMetadata.activeLastResortKyberPreKeyId = -1;
707 }
708 if (rootNode.hasNonNull("profileKey")) {
709 try {
710 profileKey = new ProfileKey(Base64.getDecoder().decode(rootNode.get("profileKey").asText()));
711 } catch (InvalidInputException e) {
712 throw new IOException(
713 "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes",
714 e);
715 }
716 }
717 if (profileKey == null) {
718 // Old config file, creating new profile key
719 setProfileKey(KeyUtils.createProfileKey());
720 }
721
722 if (previousStorageVersion < 5) {
723 final var legacyRecipientsStoreFile = new File(userPath, "recipients-store");
724 if (legacyRecipientsStoreFile.exists()) {
725 LegacyRecipientStore2.migrate(legacyRecipientsStoreFile, getRecipientStore());
726 }
727 }
728 if (previousStorageVersion < 6) {
729 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
730 }
731 final var legacyAciPreKeysPath = new File(userPath, "pre-keys");
732 if (legacyAciPreKeysPath.exists()) {
733 LegacyPreKeyStore.migrate(legacyAciPreKeysPath, aciAccountData.getPreKeyStore());
734 }
735 final var legacyPniPreKeysPath = new File(userPath, "pre-keys-pni");
736 if (legacyPniPreKeysPath.exists()) {
737 LegacyPreKeyStore.migrate(legacyPniPreKeysPath, pniAccountData.getPreKeyStore());
738 }
739 final var legacyAciSignedPreKeysPath = new File(userPath, "signed-pre-keys");
740 if (legacyAciSignedPreKeysPath.exists()) {
741 LegacySignedPreKeyStore.migrate(legacyAciSignedPreKeysPath, aciAccountData.getSignedPreKeyStore());
742 }
743 final var legacyPniSignedPreKeysPath = new File(userPath, "signed-pre-keys-pni");
744 if (legacyPniSignedPreKeysPath.exists()) {
745 LegacySignedPreKeyStore.migrate(legacyPniSignedPreKeysPath, pniAccountData.getSignedPreKeyStore());
746 }
747 final var legacySessionsPath = new File(userPath, "sessions");
748 if (legacySessionsPath.exists()) {
749 LegacySessionStore.migrate(legacySessionsPath,
750 getRecipientResolver(),
751 getRecipientAddressResolver(),
752 aciAccountData.getSessionStore());
753 }
754 final var legacyIdentitiesPath = new File(userPath, "identities");
755 if (legacyIdentitiesPath.exists()) {
756 LegacyIdentityKeyStore.migrate(legacyIdentitiesPath,
757 getRecipientResolver(),
758 getRecipientAddressResolver(),
759 getIdentityKeyStore());
760 }
761 final var legacySignalProtocolStore = rootNode.hasNonNull("axolotlStore")
762 ? jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"),
763 LegacyJsonSignalProtocolStore.class)
764 : null;
765 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
766 aciIdentityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair();
767 registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId();
768 }
769
770 this.aciAccountData.setIdentityKeyPair(aciIdentityKeyPair);
771 this.aciAccountData.setLocalRegistrationId(registrationId);
772
773 loadLegacyStores(rootNode, legacySignalProtocolStore);
774
775 final var legacySenderKeysPath = new File(userPath, "sender-keys");
776 if (legacySenderKeysPath.exists()) {
777 LegacySenderKeyRecordStore.migrate(legacySenderKeysPath,
778 getRecipientResolver(),
779 getRecipientAddressResolver(),
780 getSenderKeyStore());
781 }
782 final var legacySenderKeysSharedPath = new File(userPath, "shared-sender-keys-store");
783 if (legacySenderKeysSharedPath.exists()) {
784 LegacySenderKeySharedStore.migrate(legacySenderKeysSharedPath,
785 getRecipientResolver(),
786 getRecipientAddressResolver(),
787 getSenderKeyStore());
788 }
789 if (rootNode.hasNonNull("groupStore")) {
790 final var groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"),
791 LegacyGroupStore.Storage.class);
792 LegacyGroupStore.migrate(groupStoreStorage,
793 new File(userPath, "group-cache"),
794 getRecipientResolver(),
795 getGroupStore());
796 }
797
798 if (rootNode.hasNonNull("stickerStore")) {
799 final var storage = jsonProcessor.convertValue(rootNode.get("stickerStore"),
800 LegacyStickerStore.Storage.class);
801 LegacyStickerStore.migrate(storage, getStickerStore());
802 }
803
804 if (rootNode.hasNonNull("configurationStore")) {
805 final var configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"),
806 LegacyConfigurationStore.Storage.class);
807 LegacyConfigurationStore.migrate(configurationStoreStorage, getConfigurationStore());
808 }
809
810 loadLegacyThreadStore(rootNode);
811 }
812
813 private void loadLegacyStores(
814 final JsonNode rootNode,
815 final LegacyJsonSignalProtocolStore legacySignalProtocolStore
816 ) {
817 var legacyRecipientStoreNode = rootNode.get("recipientStore");
818 if (legacyRecipientStoreNode != null) {
819 logger.debug("Migrating legacy recipient store.");
820 var legacyRecipientStore = jsonProcessor.convertValue(legacyRecipientStoreNode, LegacyRecipientStore.class);
821 if (legacyRecipientStore != null) {
822 legacyRecipientStore.getAddresses()
823 .forEach(recipient -> getRecipientStore().resolveRecipientTrusted(recipient));
824 }
825 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
826 }
827
828 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyPreKeyStore() != null) {
829 logger.debug("Migrating legacy pre key store.");
830 aciAccountData.getPreKeyStore().removeAllPreKeys();
831 for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) {
832 try {
833 aciAccountData.getPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue()));
834 } catch (InvalidMessageException e) {
835 logger.warn("Failed to migrate pre key, ignoring", e);
836 }
837 }
838 }
839
840 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacySignedPreKeyStore() != null) {
841 logger.debug("Migrating legacy signed pre key store.");
842 aciAccountData.getSignedPreKeyStore().removeAllSignedPreKeys();
843 for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) {
844 try {
845 aciAccountData.getSignedPreKeyStore()
846 .storeSignedPreKey(entry.getKey(), new SignedPreKeyRecord(entry.getValue()));
847 } catch (InvalidMessageException e) {
848 logger.warn("Failed to migrate signed pre key, ignoring", e);
849 }
850 }
851 }
852
853 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacySessionStore() != null) {
854 logger.debug("Migrating legacy session store.");
855 for (var session : legacySignalProtocolStore.getLegacySessionStore().getSessions()) {
856 try {
857 aciAccountData.getSessionStore()
858 .storeSession(new SignalProtocolAddress(session.address.getIdentifier(), session.deviceId),
859 new SessionRecord(session.sessionRecord));
860 } catch (Exception e) {
861 logger.warn("Failed to migrate session, ignoring", e);
862 }
863 }
864 }
865
866 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
867 logger.debug("Migrating legacy identity session store.");
868 for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) {
869 if (identity.getAddress().serviceId().isEmpty()) {
870 continue;
871 }
872 final var serviceId = identity.getAddress().serviceId().get();
873 getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey());
874 getIdentityKeyStore().setIdentityTrustLevel(serviceId,
875 identity.getIdentityKey(),
876 identity.getTrustLevel());
877 }
878 }
879
880 if (rootNode.hasNonNull("contactStore")) {
881 logger.debug("Migrating legacy contact store.");
882 final var contactStoreNode = rootNode.get("contactStore");
883 final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class);
884 for (var contact : contactStore.getContacts()) {
885 final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress());
886 getContactStore().storeContact(recipientId,
887 new Contact(contact.name,
888 null,
889 null,
890 null,
891 null,
892 null,
893 contact.color,
894 contact.messageExpirationTime,
895 1,
896 0,
897 false,
898 contact.blocked,
899 contact.archived,
900 false,
901 false,
902 null));
903
904 // Store profile keys only in profile store
905 var profileKeyString = contact.profileKey;
906 if (profileKeyString != null) {
907 final ProfileKey profileKey;
908 try {
909 profileKey = new ProfileKey(Base64.getDecoder().decode(profileKeyString));
910 getProfileStore().storeProfileKey(recipientId, profileKey);
911 } catch (InvalidInputException e) {
912 logger.warn("Failed to parse legacy contact profile key: {}", e.getMessage());
913 }
914 }
915 }
916 }
917
918 if (rootNode.hasNonNull("profileStore")) {
919 logger.debug("Migrating legacy profile store.");
920 var profileStoreNode = rootNode.get("profileStore");
921 final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class);
922 for (var profileEntry : legacyProfileStore.getProfileEntries()) {
923 var recipientId = getRecipientResolver().resolveRecipient(profileEntry.address());
924 // Not migrating profile key credential here, it was changed to expiring profile key credentials
925 getProfileStore().storeProfileKey(recipientId, profileEntry.profileKey());
926 final var profile = profileEntry.profile();
927 if (profile != null) {
928 final var capabilities = new HashSet<Profile.Capability>();
929 if (profile.getCapabilities() != null) {
930 if (profile.getCapabilities().storage) {
931 capabilities.add(Profile.Capability.storage);
932 }
933 }
934 final var newProfile = new Profile(profileEntry.lastUpdateTimestamp(),
935 profile.getGivenName(),
936 profile.getFamilyName(),
937 profile.getAbout(),
938 profile.getAboutEmoji(),
939 null,
940 null,
941 profile.isUnrestrictedUnidentifiedAccess()
942 ? Profile.UnidentifiedAccessMode.UNRESTRICTED
943 : profile.getUnidentifiedAccess() != null
944 ? Profile.UnidentifiedAccessMode.ENABLED
945 : Profile.UnidentifiedAccessMode.DISABLED,
946 capabilities,
947 null);
948 getProfileStore().storeProfile(recipientId, newProfile);
949 }
950 }
951 }
952 }
953
954 private void loadLegacyThreadStore(final JsonNode rootNode) {
955 var threadStoreNode = rootNode.get("threadStore");
956 if (threadStoreNode != null && !threadStoreNode.isNull()) {
957 var threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class);
958 // Migrate thread info to group and contact store
959 for (var thread : threadStore.getThreads()) {
960 if (thread.id == null || thread.id.isEmpty()) {
961 continue;
962 }
963 try {
964 if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) {
965 final var recipientId = getRecipientResolver().resolveRecipient(thread.id);
966 var contact = getContactStore().getContact(recipientId);
967 if (contact != null) {
968 getContactStore().storeContact(recipientId,
969 Contact.newBuilder(contact)
970 .withMessageExpirationTime(thread.messageExpirationTime)
971 .withMessageExpirationTimeVersion(1)
972 .build());
973 }
974 } else {
975 var groupInfo = getGroupStore().getGroup(GroupId.fromBase64(thread.id));
976 if (groupInfo instanceof GroupInfoV1) {
977 ((GroupInfoV1) groupInfo).messageExpirationTime = thread.messageExpirationTime;
978 getGroupStore().updateGroup(groupInfo);
979 }
980 }
981 } catch (Exception e) {
982 logger.warn("Failed to read legacy thread info: {}", e.getMessage());
983 }
984 }
985 }
986 }
987
988 private void save() {
989 synchronized (fileChannel) {
990 final var base64 = Base64.getEncoder();
991 final var storage = new Storage(CURRENT_STORAGE_VERSION,
992 System.currentTimeMillis(),
993 serviceEnvironment.name(),
994 registered,
995 number,
996 username,
997 encryptedDeviceName,
998 deviceId,
999 isMultiDevice,
1000 password,
1001 Storage.AccountData.from(aciAccountData),
1002 Storage.AccountData.from(pniAccountData),
1003 registrationLockPin,
1004 pinMasterKey == null ? null : base64.encodeToString(pinMasterKey.serialize()),
1005 storageKey == null ? null : base64.encodeToString(storageKey.serialize()),
1006 accountEntropyPool == null ? null : accountEntropyPool.getValue(),
1007 mediaRootBackupKey == null ? null : base64.encodeToString(mediaRootBackupKey.getValue()),
1008 profileKey == null ? null : base64.encodeToString(profileKey.serialize()),
1009 usernameLink == null ? null : base64.encodeToString(usernameLink.getEntropy()),
1010 usernameLink == null ? null : usernameLink.getServerId().toString());
1011 try {
1012 try (var output = new ByteArrayOutputStream()) {
1013 // Write to memory first to prevent corrupting the file in case of serialization errors
1014 jsonProcessor.writeValue(output, storage);
1015 var input = new ByteArrayInputStream(output.toByteArray());
1016 fileChannel.position(0);
1017 input.transferTo(Channels.newOutputStream(fileChannel));
1018 fileChannel.truncate(fileChannel.position());
1019 fileChannel.force(false);
1020 }
1021 } catch (Exception e) {
1022 logger.error("Error saving file: {}", e.getMessage(), e);
1023 }
1024 }
1025 }
1026
1027 private static Pair<FileChannel, FileLock> openFileChannel(File fileName, boolean waitForLock) throws IOException {
1028 var fileChannel = new RandomAccessFile(fileName, "rw").getChannel();
1029 try {
1030 var lock = fileChannel.tryLock();
1031 if (lock == null) {
1032 if (!waitForLock) {
1033 logger.debug("Config file is in use by another instance.");
1034 throw new IOException("Config file is in use by another instance.");
1035 }
1036 logger.info("Config file is in use by another instance, waiting…");
1037 lock = fileChannel.lock();
1038 logger.info("Config file lock acquired.");
1039 }
1040 final var result = new Pair<>(fileChannel, lock);
1041 fileChannel = null;
1042 return result;
1043 } finally {
1044 if (fileChannel != null) {
1045 fileChannel.close();
1046 }
1047 }
1048 }
1049
1050 private void clearAllPreKeys() {
1051 clearAllPreKeys(ServiceIdType.ACI);
1052 clearAllPreKeys(ServiceIdType.PNI);
1053 }
1054
1055 private void initAllPreKeyIds() {
1056 resetPreKeyOffsets(ServiceIdType.ACI);
1057 resetPreKeyOffsets(ServiceIdType.PNI);
1058 resetKyberPreKeyOffsets(ServiceIdType.ACI);
1059 resetKyberPreKeyOffsets(ServiceIdType.PNI);
1060 }
1061
1062 private void clearAllPreKeys(ServiceIdType serviceIdType) {
1063 final var accountData = getAccountData(serviceIdType);
1064 resetPreKeyOffsets(serviceIdType);
1065 resetKyberPreKeyOffsets(serviceIdType);
1066 accountData.getPreKeyStore().removeAllPreKeys();
1067 accountData.getSignedPreKeyStore().removeAllSignedPreKeys();
1068 accountData.getKyberPreKeyStore().removeAllKyberPreKeys();
1069 save();
1070 }
1071
1072 private void setPreKeys(ServiceIdType serviceIdType, PreKeyCollection preKeyCollection) {
1073 final var accountData = getAccountData(serviceIdType);
1074 final var preKeyMetadata = accountData.getPreKeyMetadata();
1075 preKeyMetadata.nextSignedPreKeyId = preKeyCollection.getSignedPreKey().getId();
1076 preKeyMetadata.nextKyberPreKeyId = preKeyCollection.getLastResortKyberPreKey().getId();
1077
1078 accountData.getPreKeyStore().removeAllPreKeys();
1079 accountData.getSignedPreKeyStore().removeAllSignedPreKeys();
1080 accountData.getKyberPreKeyStore().removeAllKyberPreKeys();
1081
1082 addSignedPreKey(serviceIdType, preKeyCollection.getSignedPreKey());
1083 addLastResortKyberPreKey(serviceIdType, preKeyCollection.getLastResortKyberPreKey());
1084
1085 save();
1086 }
1087
1088 public void resetPreKeyOffsets(final ServiceIdType serviceIdType) {
1089 final var preKeyMetadata = getAccountData(serviceIdType).getPreKeyMetadata();
1090 preKeyMetadata.nextPreKeyId = getRandomPreKeyIdOffset();
1091 preKeyMetadata.nextSignedPreKeyId = getRandomPreKeyIdOffset();
1092 preKeyMetadata.activeSignedPreKeyId = -1;
1093 save();
1094 }
1095
1096 private static int getRandomPreKeyIdOffset() {
1097 return KeyUtils.getRandomInt(PREKEY_MAXIMUM_ID);
1098 }
1099
1100 public void addPreKeys(ServiceIdType serviceIdType, List<PreKeyRecord> records) {
1101 final var accountData = getAccountData(serviceIdType);
1102 final var preKeyMetadata = accountData.getPreKeyMetadata();
1103 logger.debug("Adding {} {} pre keys with offset {}",
1104 records.size(),
1105 serviceIdType,
1106 preKeyMetadata.nextPreKeyId);
1107 accountData.getSignalServiceAccountDataStore()
1108 .markAllOneTimeEcPreKeysStaleIfNecessary(System.currentTimeMillis());
1109 for (var record : records) {
1110 if (preKeyMetadata.nextPreKeyId != record.getId()) {
1111 logger.error("Invalid pre key id {}, expected {}", record.getId(), preKeyMetadata.nextPreKeyId);
1112 throw new AssertionError("Invalid pre key id");
1113 }
1114 accountData.getPreKeyStore().storePreKey(record.getId(), record);
1115 preKeyMetadata.nextPreKeyId = (preKeyMetadata.nextPreKeyId + 1) % PREKEY_MAXIMUM_ID;
1116 }
1117 save();
1118 }
1119
1120 public void addSignedPreKey(ServiceIdType serviceIdType, SignedPreKeyRecord record) {
1121 final var accountData = getAccountData(serviceIdType);
1122 final var preKeyMetadata = accountData.getPreKeyMetadata();
1123 logger.debug("Adding {} signed pre key with offset {}", serviceIdType, preKeyMetadata.nextSignedPreKeyId);
1124 if (preKeyMetadata.nextSignedPreKeyId != record.getId()) {
1125 logger.error("Invalid signed pre key id {}, expected {}",
1126 record.getId(),
1127 preKeyMetadata.nextSignedPreKeyId);
1128 throw new AssertionError("Invalid signed pre key id");
1129 }
1130 accountData.getSignedPreKeyStore().storeSignedPreKey(record.getId(), record);
1131 preKeyMetadata.nextSignedPreKeyId = (preKeyMetadata.nextSignedPreKeyId + 1) % PREKEY_MAXIMUM_ID;
1132 preKeyMetadata.activeSignedPreKeyId = record.getId();
1133 save();
1134 }
1135
1136 public void resetKyberPreKeyOffsets(final ServiceIdType serviceIdType) {
1137 final var preKeyMetadata = getAccountData(serviceIdType).getPreKeyMetadata();
1138 preKeyMetadata.nextKyberPreKeyId = getRandomPreKeyIdOffset();
1139 preKeyMetadata.activeLastResortKyberPreKeyId = -1;
1140 save();
1141 }
1142
1143 public void addKyberPreKeys(ServiceIdType serviceIdType, List<KyberPreKeyRecord> records) {
1144 final var accountData = getAccountData(serviceIdType);
1145 final var preKeyMetadata = accountData.getPreKeyMetadata();
1146 logger.debug("Adding {} {} kyber pre keys with offset {}",
1147 records.size(),
1148 serviceIdType,
1149 preKeyMetadata.nextKyberPreKeyId);
1150 accountData.getSignalServiceAccountDataStore()
1151 .markAllOneTimeKyberPreKeysStaleIfNecessary(System.currentTimeMillis());
1152 for (var record : records) {
1153 if (preKeyMetadata.nextKyberPreKeyId != record.getId()) {
1154 logger.error("Invalid kyber pre key id {}, expected {}",
1155 record.getId(),
1156 preKeyMetadata.nextKyberPreKeyId);
1157 throw new AssertionError("Invalid kyber pre key id");
1158 }
1159 accountData.getKyberPreKeyStore().storeKyberPreKey(record.getId(), record);
1160 preKeyMetadata.nextKyberPreKeyId = (preKeyMetadata.nextKyberPreKeyId + 1) % PREKEY_MAXIMUM_ID;
1161 }
1162 save();
1163 }
1164
1165 public void addLastResortKyberPreKey(ServiceIdType serviceIdType, KyberPreKeyRecord record) {
1166 final var accountData = getAccountData(serviceIdType);
1167 final var preKeyMetadata = accountData.getPreKeyMetadata();
1168 logger.debug("Adding {} last resort kyber pre key with offset {}",
1169 serviceIdType,
1170 preKeyMetadata.nextKyberPreKeyId);
1171 if (preKeyMetadata.nextKyberPreKeyId != record.getId()) {
1172 logger.error("Invalid last resort kyber pre key id {}, expected {}",
1173 record.getId(),
1174 preKeyMetadata.nextKyberPreKeyId);
1175 throw new AssertionError("Invalid last resort kyber pre key id");
1176 }
1177 accountData.getKyberPreKeyStore().storeLastResortKyberPreKey(record.getId(), record);
1178 preKeyMetadata.activeLastResortKyberPreKeyId = record.getId();
1179 preKeyMetadata.nextKyberPreKeyId = (preKeyMetadata.nextKyberPreKeyId + 1) % PREKEY_MAXIMUM_ID;
1180 save();
1181 }
1182
1183 public int getPreviousStorageVersion() {
1184 return previousStorageVersion;
1185 }
1186
1187 public AccountData<? extends ServiceId> getAccountData(ServiceIdType serviceIdType) {
1188 return switch (serviceIdType) {
1189 case ACI -> aciAccountData;
1190 case PNI -> pniAccountData;
1191 };
1192 }
1193
1194 public AccountData<? extends ServiceId> getAccountData(ServiceId accountIdentifier) {
1195 if (accountIdentifier.equals(aciAccountData.getServiceId())) {
1196 return aciAccountData;
1197 } else if (accountIdentifier.equals(pniAccountData.getServiceId())) {
1198 return pniAccountData;
1199 } else {
1200 throw new IllegalArgumentException("No matching account data found for " + accountIdentifier);
1201 }
1202 }
1203
1204 public SignalServiceDataStore getSignalServiceDataStore() {
1205 return new SignalServiceDataStore() {
1206 @Override
1207 public SignalServiceAccountDataStore get(final ServiceId accountIdentifier) {
1208 return getAccountData(accountIdentifier).getSignalServiceAccountDataStore();
1209 }
1210
1211 @Override
1212 public SignalServiceAccountDataStore aci() {
1213 return aciAccountData.getSignalServiceAccountDataStore();
1214 }
1215
1216 @Override
1217 public SignalServiceAccountDataStore pni() {
1218 return pniAccountData.getSignalServiceAccountDataStore();
1219 }
1220
1221 @Override
1222 public boolean isMultiDevice() {
1223 return SignalAccount.this.isMultiDevice();
1224 }
1225 };
1226 }
1227
1228 public IdentityKeyStore getIdentityKeyStore() {
1229 return getOrCreate(() -> identityKeyStore,
1230 () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(),
1231 settings.trustNewIdentity(),
1232 getRecipientStore()));
1233 }
1234
1235 public GroupStore getGroupStore() {
1236 return getOrCreate(() -> groupStore,
1237 () -> groupStore = new GroupStore(getAccountDatabase(),
1238 getRecipientResolver(),
1239 getRecipientIdCreator()));
1240 }
1241
1242 public ContactsStore getContactStore() {
1243 return getRecipientStore();
1244 }
1245
1246 public CdsiStore getCdsiStore() {
1247 return getOrCreate(() -> cdsiStore, () -> cdsiStore = new CdsiStore(getAccountDatabase()));
1248 }
1249
1250 private RecipientIdCreator getRecipientIdCreator() {
1251 return recipientId -> getRecipientStore().create(recipientId);
1252 }
1253
1254 public RecipientResolver getRecipientResolver() {
1255 return new RecipientResolver.RecipientResolverWrapper(this::getRecipientStore);
1256 }
1257
1258 public RecipientTrustedResolver getRecipientTrustedResolver() {
1259 return new RecipientTrustedResolver.RecipientTrustedResolverWrapper(this::getRecipientStore);
1260 }
1261
1262 public RecipientAddressResolver getRecipientAddressResolver() {
1263 return recipientId -> getRecipientStore().resolveRecipientAddress(recipientId);
1264 }
1265
1266 public RecipientStore getRecipientStore() {
1267 return getOrCreate(() -> recipientStore,
1268 () -> recipientStore = new RecipientStore(this::mergeRecipients,
1269 this::getSelfRecipientAddress,
1270 this::getProfileKey,
1271 getAccountDatabase()));
1272 }
1273
1274 public ProfileStore getProfileStore() {
1275 return getRecipientStore();
1276 }
1277
1278 public StickerStore getStickerStore() {
1279 return getOrCreate(() -> stickerStore, () -> stickerStore = new StickerStore(getAccountDatabase()));
1280 }
1281
1282 public SenderKeyStore getSenderKeyStore() {
1283 return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getAccountDatabase()));
1284 }
1285
1286 private KeyValueStore getKeyValueStore() {
1287 return getOrCreate(() -> keyValueStore, () -> keyValueStore = new KeyValueStore(getAccountDatabase()));
1288 }
1289
1290 public UnknownStorageIdStore getUnknownStorageIdStore() {
1291 return getOrCreate(() -> unknownStorageIdStore, () -> unknownStorageIdStore = new UnknownStorageIdStore());
1292 }
1293
1294 public ConfigurationStore getConfigurationStore() {
1295 return getOrCreate(() -> configurationStore,
1296 () -> configurationStore = new ConfigurationStore(getKeyValueStore(), getRecipientStore()));
1297 }
1298
1299 public MessageCache getMessageCache() {
1300 return getOrCreate(() -> messageCache,
1301 () -> messageCache = new MessageCache(getMessageCachePath(dataPath, accountPath)));
1302 }
1303
1304 public AccountDatabase getAccountDatabase() {
1305 return getOrCreate(() -> accountDatabase, () -> {
1306 try {
1307 accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, accountPath));
1308 } catch (SQLException e) {
1309 throw new RuntimeException(e);
1310 }
1311 });
1312 }
1313
1314 public MessageSendLogStore getMessageSendLogStore() {
1315 return getOrCreate(() -> messageSendLogStore,
1316 () -> messageSendLogStore = new MessageSendLogStore(getAccountDatabase(),
1317 settings.disableMessageSendLog()));
1318 }
1319
1320 public CredentialsProvider getCredentialsProvider() {
1321 return new CredentialsProvider() {
1322 @Override
1323 public ACI getAci() {
1324 return aciAccountData.getServiceId();
1325 }
1326
1327 @Override
1328 public PNI getPni() {
1329 return pniAccountData.getServiceId();
1330 }
1331
1332 @Override
1333 public String getE164() {
1334 return number;
1335 }
1336
1337 @Override
1338 public String getPassword() {
1339 return password;
1340 }
1341
1342 @Override
1343 public int getDeviceId() {
1344 return deviceId;
1345 }
1346 };
1347 }
1348
1349 public String getNumber() {
1350 return number;
1351 }
1352
1353 public void setNumber(final String number) {
1354 this.number = number;
1355 save();
1356 }
1357
1358 public String getUsername() {
1359 return username;
1360 }
1361
1362 public void setUsername(final String username) {
1363 this.username = username;
1364 save();
1365 }
1366
1367 public UsernameLinkComponents getUsernameLink() {
1368 return usernameLink;
1369 }
1370
1371 public void setUsernameLink(final UsernameLinkComponents usernameLink) {
1372 this.usernameLink = usernameLink;
1373 save();
1374 }
1375
1376 public ServiceEnvironment getServiceEnvironment() {
1377 return serviceEnvironment;
1378 }
1379
1380 public void setServiceEnvironment(final ServiceEnvironment serviceEnvironment) {
1381 this.serviceEnvironment = serviceEnvironment;
1382 save();
1383 }
1384
1385 public AccountAttributes getAccountAttributes(String registrationLock) {
1386 return new AccountAttributes(null,
1387 aciAccountData.getLocalRegistrationId(),
1388 false,
1389 false,
1390 true,
1391 registrationLock != null ? registrationLock : getRegistrationLock(),
1392 getSelfUnidentifiedAccessKey(),
1393 isUnrestrictedUnidentifiedAccess(),
1394 isDiscoverableByPhoneNumber(),
1395 getAccountCapabilities(),
1396 encryptedDeviceName,
1397 pniAccountData.getLocalRegistrationId(),
1398 getRecoveryPassword());
1399 }
1400
1401 public AccountAttributes.Capabilities getAccountCapabilities() {
1402 return getCapabilities(isPrimaryDevice());
1403 }
1404
1405 public ServiceId getAccountId(ServiceIdType serviceIdType) {
1406 return getAccountData(serviceIdType).getServiceId();
1407 }
1408
1409 public ACI getAci() {
1410 return aciAccountData.getServiceId();
1411 }
1412
1413 public void setAci(final ACI aci) {
1414 this.aciAccountData.setServiceId(aci);
1415 save();
1416 }
1417
1418 public PNI getPni() {
1419 return pniAccountData.getServiceId();
1420 }
1421
1422 public void setPni(final PNI updatedPni) {
1423 final var oldPni = pniAccountData.getServiceId();
1424 if (oldPni != null && !oldPni.equals(updatedPni)) {
1425 // Clear data for old PNI
1426 identityKeyStore.deleteIdentity(oldPni);
1427 }
1428
1429 this.pniAccountData.setServiceId(updatedPni);
1430 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
1431 trustSelfIdentity(ServiceIdType.PNI);
1432 save();
1433 }
1434
1435 public void setNewPniIdentity(
1436 final IdentityKeyPair pniIdentityKeyPair,
1437 final SignedPreKeyRecord pniSignedPreKey,
1438 final KyberPreKeyRecord lastResortKyberPreKey,
1439 final int localPniRegistrationId
1440 ) {
1441 setPniIdentityKeyPair(pniIdentityKeyPair);
1442 pniAccountData.setLocalRegistrationId(localPniRegistrationId);
1443
1444 final AccountData<? extends ServiceId> accountData = getAccountData(ServiceIdType.PNI);
1445 final var preKeyMetadata = accountData.getPreKeyMetadata();
1446 preKeyMetadata.nextSignedPreKeyId = pniSignedPreKey.getId();
1447 accountData.getSignedPreKeyStore().removeSignedPreKey(pniSignedPreKey.getId());
1448 addSignedPreKey(ServiceIdType.PNI, pniSignedPreKey);
1449 if (lastResortKyberPreKey != null) {
1450 preKeyMetadata.nextKyberPreKeyId = lastResortKyberPreKey.getId();
1451 accountData.getKyberPreKeyStore().removeKyberPreKey(lastResortKyberPreKey.getId());
1452 addLastResortKyberPreKey(ServiceIdType.PNI, lastResortKyberPreKey);
1453 }
1454 save();
1455 }
1456
1457 public SignalServiceAddress getSelfAddress() {
1458 return new SignalServiceAddress(getAci(), number);
1459 }
1460
1461 public RecipientAddress getSelfRecipientAddress() {
1462 return new RecipientAddress(getAci(), getPni(), number, username);
1463 }
1464
1465 public RecipientId getSelfRecipientId() {
1466 return selfRecipientId;
1467 }
1468
1469 public Profile getSelfRecipientProfile() {
1470 return recipientStore.getProfile(selfRecipientId);
1471 }
1472
1473 public String getSessionId(final String forNumber) {
1474 final var keyValueStore = getKeyValueStore();
1475 final var sessionNumber = keyValueStore.getEntry(verificationSessionNumber);
1476 if (!forNumber.equals(sessionNumber)) {
1477 return null;
1478 }
1479 return keyValueStore.getEntry(verificationSessionId);
1480 }
1481
1482 public void setSessionId(final String sessionNumber, final String sessionId) {
1483 final var keyValueStore = getKeyValueStore();
1484 keyValueStore.storeEntry(verificationSessionNumber, sessionNumber);
1485 keyValueStore.storeEntry(verificationSessionId, sessionId);
1486 }
1487
1488 public void setEncryptedDeviceName(final String encryptedDeviceName) {
1489 this.encryptedDeviceName = encryptedDeviceName;
1490 save();
1491 }
1492
1493 public int getDeviceId() {
1494 return deviceId;
1495 }
1496
1497 public boolean isPrimaryDevice() {
1498 return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
1499 }
1500
1501 public IdentityKeyPair getIdentityKeyPair(ServiceIdType serviceIdType) {
1502 return getAccountData(serviceIdType).getIdentityKeyPair();
1503 }
1504
1505 public IdentityKeyPair getAciIdentityKeyPair() {
1506 return aciAccountData.getIdentityKeyPair();
1507 }
1508
1509 public IdentityKeyPair getPniIdentityKeyPair() {
1510 return pniAccountData.getIdentityKeyPair();
1511 }
1512
1513 public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
1514 pniAccountData.setIdentityKeyPair(identityKeyPair);
1515 trustSelfIdentity(ServiceIdType.PNI);
1516 save();
1517 }
1518
1519 public String getPassword() {
1520 return password;
1521 }
1522
1523 public void setRegistrationLockPin(final String registrationLockPin) {
1524 this.registrationLockPin = registrationLockPin;
1525 save();
1526 }
1527
1528 public String getRegistrationLockPin() {
1529 return registrationLockPin;
1530 }
1531
1532 public String getRegistrationLock() {
1533 final var masterKey = getPinBackedMasterKey();
1534 if (masterKey == null) {
1535 return null;
1536 }
1537 return masterKey.deriveRegistrationLock();
1538 }
1539
1540 public MasterKey getPinBackedMasterKey() {
1541 if (registrationLockPin == null) {
1542 return null;
1543 } else if (!isPrimaryDevice()) {
1544 return getMasterKey();
1545 }
1546 return getOrCreatePinMasterKey();
1547 }
1548
1549 public MasterKey getOrCreatePinMasterKey() {
1550 final var key = getMasterKey();
1551 if (key != null) {
1552 return key;
1553 }
1554
1555 return getOrCreateAccountEntropyPool().deriveMasterKey();
1556 }
1557
1558 private MasterKey getMasterKey() {
1559 if (pinMasterKey != null) {
1560 return pinMasterKey;
1561 } else if (accountEntropyPool != null) {
1562 return accountEntropyPool.deriveMasterKey();
1563 }
1564 return null;
1565 }
1566
1567 public void setMasterKey(MasterKey masterKey) {
1568 if (isPrimaryDevice()) {
1569 return;
1570 }
1571 this.pinMasterKey = masterKey;
1572 if (masterKey != null) {
1573 this.storageKey = null;
1574 }
1575 save();
1576 }
1577
1578 public StorageKey getOrCreateStorageKey() {
1579 if (storageKey != null) {
1580 return storageKey;
1581 } else if (pinMasterKey != null) {
1582 return pinMasterKey.deriveStorageServiceKey();
1583 } else if (accountEntropyPool != null) {
1584 return accountEntropyPool.deriveMasterKey().deriveStorageServiceKey();
1585 } else if (!isPrimaryDevice() || !isMultiDevice()) {
1586 // Only upload storage, if a pin master key already exists or linked devices exist
1587 return null;
1588 }
1589
1590 return getOrCreatePinMasterKey().deriveStorageServiceKey();
1591 }
1592
1593 public void setStorageKey(final StorageKey storageKey) {
1594 if (isPrimaryDevice() || storageKey.equals(this.storageKey)) {
1595 return;
1596 }
1597 this.storageKey = storageKey;
1598 save();
1599 }
1600
1601 public AccountEntropyPool getOrCreateAccountEntropyPool() {
1602 if (accountEntropyPool == null) {
1603 accountEntropyPool = AccountEntropyPool.Companion.generate();
1604 save();
1605 }
1606 return accountEntropyPool;
1607 }
1608
1609 public void setAccountEntropyPool(final AccountEntropyPool accountEntropyPool) {
1610 this.accountEntropyPool = accountEntropyPool;
1611 if (accountEntropyPool != null) {
1612 this.storageKey = null;
1613 this.pinMasterKey = null;
1614 }
1615 save();
1616 }
1617
1618 public boolean needsStorageKeyMigration() {
1619 return isPrimaryDevice() && (storageKey != null || pinMasterKey != null);
1620 }
1621
1622 public MediaRootBackupKey getOrCreateMediaRootBackupKey() {
1623 if (mediaRootBackupKey == null) {
1624 mediaRootBackupKey = KeyUtils.createMediaRootBackupKey();
1625 save();
1626 }
1627 return mediaRootBackupKey;
1628 }
1629
1630 public void setMediaRootBackupKey(final MediaRootBackupKey mediaRootBackupKey) {
1631 this.mediaRootBackupKey = mediaRootBackupKey;
1632 save();
1633 }
1634
1635 public String getRecoveryPassword() {
1636 final var masterKey = getPinBackedMasterKey();
1637 if (masterKey == null) {
1638 return null;
1639 }
1640 return masterKey.deriveRegistrationRecoveryPassword();
1641 }
1642
1643 public long getStorageManifestVersion() {
1644 return getKeyValueStore().getEntry(storageManifestVersion);
1645 }
1646
1647 public void setStorageManifestVersion(final long value) {
1648 getKeyValueStore().storeEntry(storageManifestVersion, value);
1649 }
1650
1651 public Optional<SignalStorageManifest> getStorageManifest() {
1652 final var storageManifestFile = getStorageManifestFile(dataPath, accountPath);
1653 if (!storageManifestFile.exists()) {
1654 return Optional.empty();
1655 }
1656 try (var inputStream = new FileInputStream(storageManifestFile)) {
1657 return Optional.of(SignalStorageManifest.Companion.deserialize(inputStream.readAllBytes()));
1658 } catch (IOException e) {
1659 logger.warn("Failed to read local storage manifest.", e);
1660 return Optional.empty();
1661 }
1662 }
1663
1664 public void setStorageManifest(SignalStorageManifest manifest) {
1665 final var storageManifestFile = getStorageManifestFile(dataPath, accountPath);
1666 if (manifest == null) {
1667 if (storageManifestFile.exists()) {
1668 try {
1669 Files.delete(storageManifestFile.toPath());
1670 } catch (IOException e) {
1671 logger.error("Failed to delete local storage manifest.", e);
1672 }
1673 }
1674 return;
1675 }
1676
1677 final var manifestBytes = manifest.serialize();
1678 try (var outputStream = new FileOutputStream(storageManifestFile)) {
1679 outputStream.write(manifestBytes);
1680 } catch (IOException e) {
1681 logger.error("Failed to store local storage manifest.", e);
1682 }
1683 }
1684
1685 public byte[] getCdsiToken() {
1686 return getKeyValueStore().getEntry(cdsiToken);
1687 }
1688
1689 public void setCdsiToken(final byte[] value) {
1690 getKeyValueStore().storeEntry(cdsiToken, value);
1691 }
1692
1693 public Long getLastRecipientsRefresh() {
1694 return getKeyValueStore().getEntry(lastRecipientsRefresh);
1695 }
1696
1697 public void setLastRecipientsRefresh(final Long value) {
1698 getKeyValueStore().storeEntry(lastRecipientsRefresh, value);
1699 }
1700
1701 public ProfileKey getProfileKey() {
1702 return profileKey;
1703 }
1704
1705 public void setProfileKey(final ProfileKey profileKey) {
1706 if (profileKey.equals(this.profileKey)) {
1707 return;
1708 }
1709 this.profileKey = profileKey;
1710 save();
1711 }
1712
1713 public byte[] getSelfUnidentifiedAccessKey() {
1714 return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey());
1715 }
1716
1717 public boolean isRegistered() {
1718 return registered;
1719 }
1720
1721 public void setRegistered(final boolean registered) {
1722 this.registered = registered;
1723 save();
1724 }
1725
1726 public boolean isMultiDevice() {
1727 return isMultiDevice;
1728 }
1729
1730 public void setMultiDevice(final boolean multiDevice) {
1731 if (isMultiDevice == multiDevice) {
1732 return;
1733 }
1734 isMultiDevice = multiDevice;
1735 save();
1736 }
1737
1738 public long getLastReceiveTimestamp() {
1739 return getKeyValueStore().getEntry(lastReceiveTimestamp);
1740 }
1741
1742 public void setLastReceiveTimestamp(final long value) {
1743 getKeyValueStore().storeEntry(lastReceiveTimestamp, value);
1744 }
1745
1746 public void setNeedsToRetryFailedMessages(final boolean value) {
1747 getKeyValueStore().storeEntry(needsToRetryFailedMessages, value);
1748 }
1749
1750 public boolean getNeedsToRetryFailedMessages() {
1751 return getKeyValueStore().getEntry(needsToRetryFailedMessages);
1752 }
1753
1754 public boolean isUnrestrictedUnidentifiedAccess() {
1755 return Boolean.TRUE.equals(getKeyValueStore().getEntry(unrestrictedUnidentifiedAccess));
1756 }
1757
1758 public void setUnrestrictedUnidentifiedAccess(boolean value) {
1759 getKeyValueStore().storeEntry(unrestrictedUnidentifiedAccess, value);
1760 }
1761
1762 public boolean isDiscoverableByPhoneNumber() {
1763 final var phoneNumberUnlisted = getConfigurationStore().getPhoneNumberUnlisted();
1764 return phoneNumberUnlisted == null || !phoneNumberUnlisted;
1765 }
1766
1767 private void trustSelfIdentity(ServiceIdType serviceIdType) {
1768 final var accountData = getAccountData(serviceIdType);
1769 final var serviceId = accountData.getServiceId();
1770 final var identityKeyPair = accountData.getIdentityKeyPair();
1771 if (serviceId == null || identityKeyPair == null) {
1772 return;
1773 }
1774 final var publicKey = identityKeyPair.getPublicKey();
1775 getIdentityKeyStore().saveIdentity(serviceId, publicKey);
1776 getIdentityKeyStore().setIdentityTrustLevel(serviceId, publicKey, TrustLevel.TRUSTED_VERIFIED);
1777 }
1778
1779 public void deleteAccountData() throws IOException {
1780 close();
1781 try (final var files = Files.walk(getUserPath(dataPath, accountPath).toPath())
1782 .sorted(Comparator.reverseOrder())) {
1783 for (final var file = files.iterator(); file.hasNext(); ) {
1784 Files.delete(file.next());
1785 }
1786 }
1787 Files.delete(getFileName(dataPath, accountPath).toPath());
1788 }
1789
1790 @Override
1791 public void close() {
1792 synchronized (fileChannel) {
1793 if (accountDatabase != null) {
1794 accountDatabase.close();
1795 }
1796 if (messageSendLogStore != null) {
1797 messageSendLogStore.close();
1798 }
1799 try {
1800 try {
1801 lock.close();
1802 } catch (ClosedChannelException ignored) {
1803 }
1804 fileChannel.close();
1805 } catch (IOException e) {
1806 logger.warn("Failed to close account: {}", e.getMessage(), e);
1807 }
1808 }
1809 }
1810
1811 private <T> T getOrCreate(Supplier<T> supplier, Callable creator) {
1812 var value = supplier.get();
1813 if (value != null) {
1814 return value;
1815 }
1816
1817 synchronized (LOCK) {
1818 value = supplier.get();
1819 if (value != null) {
1820 return value;
1821 }
1822 creator.call();
1823 return supplier.get();
1824 }
1825 }
1826
1827 private interface Callable {
1828
1829 void call();
1830 }
1831
1832 public static class PreKeyMetadata {
1833
1834 private int nextPreKeyId = 1;
1835 private int nextSignedPreKeyId = 1;
1836 private int activeSignedPreKeyId = -1;
1837 private int nextKyberPreKeyId = 1;
1838 private int activeLastResortKyberPreKeyId = -1;
1839
1840 public int getNextPreKeyId() {
1841 return nextPreKeyId;
1842 }
1843
1844 public int getNextSignedPreKeyId() {
1845 return nextSignedPreKeyId;
1846 }
1847
1848 public int getActiveSignedPreKeyId() {
1849 return activeSignedPreKeyId;
1850 }
1851
1852 public int getNextKyberPreKeyId() {
1853 return nextKyberPreKeyId;
1854 }
1855
1856 public int getActiveLastResortKyberPreKeyId() {
1857 return activeLastResortKyberPreKeyId;
1858 }
1859 }
1860
1861 public class AccountData<SERVICE_ID extends ServiceId> {
1862
1863 private final ServiceIdType serviceIdType;
1864 private SERVICE_ID serviceId;
1865 private IdentityKeyPair identityKeyPair;
1866 private int localRegistrationId;
1867 private final PreKeyMetadata preKeyMetadata = new PreKeyMetadata();
1868
1869 private SignalProtocolStore signalProtocolStore;
1870 private PreKeyStore preKeyStore;
1871 private SignedPreKeyStore signedPreKeyStore;
1872 private KyberPreKeyStore kyberPreKeyStore;
1873 private SessionStore sessionStore;
1874 private SignalIdentityKeyStore identityKeyStore;
1875
1876 private AccountData(final ServiceIdType serviceIdType) {
1877 this.serviceIdType = serviceIdType;
1878 }
1879
1880 public SERVICE_ID getServiceId() {
1881 return serviceId;
1882 }
1883
1884 private void setServiceId(final SERVICE_ID serviceId) {
1885 this.serviceId = serviceId;
1886 }
1887
1888 public IdentityKeyPair getIdentityKeyPair() {
1889 return identityKeyPair;
1890 }
1891
1892 private void setIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
1893 this.identityKeyPair = identityKeyPair;
1894 }
1895
1896 public int getLocalRegistrationId() {
1897 return localRegistrationId;
1898 }
1899
1900 private void setLocalRegistrationId(final int localRegistrationId) {
1901 this.localRegistrationId = localRegistrationId;
1902 this.identityKeyStore = null;
1903 }
1904
1905 public PreKeyMetadata getPreKeyMetadata() {
1906 return preKeyMetadata;
1907 }
1908
1909 private SignalServiceAccountDataStore getSignalServiceAccountDataStore() {
1910 return getOrCreate(() -> signalProtocolStore,
1911 () -> signalProtocolStore = new SignalProtocolStore(getPreKeyStore(),
1912 getSignedPreKeyStore(),
1913 getKyberPreKeyStore(),
1914 getSessionStore(),
1915 getIdentityKeyStore(),
1916 getSenderKeyStore(),
1917 SignalAccount.this::isMultiDevice));
1918 }
1919
1920 public PreKeyStore getPreKeyStore() {
1921 return getOrCreate(() -> preKeyStore,
1922 () -> preKeyStore = new PreKeyStore(getAccountDatabase(), serviceIdType));
1923 }
1924
1925 public SignedPreKeyStore getSignedPreKeyStore() {
1926 return getOrCreate(() -> signedPreKeyStore,
1927 () -> signedPreKeyStore = new SignedPreKeyStore(getAccountDatabase(), serviceIdType));
1928 }
1929
1930 public KyberPreKeyStore getKyberPreKeyStore() {
1931 return getOrCreate(() -> kyberPreKeyStore,
1932 () -> kyberPreKeyStore = new KyberPreKeyStore(getAccountDatabase(), serviceIdType));
1933 }
1934
1935 public SessionStore getSessionStore() {
1936 return getOrCreate(() -> sessionStore,
1937 () -> sessionStore = new SessionStore(getAccountDatabase(), serviceIdType));
1938 }
1939
1940 public SignalIdentityKeyStore getIdentityKeyStore() {
1941 return getOrCreate(() -> identityKeyStore,
1942 () -> identityKeyStore = new SignalIdentityKeyStore(() -> identityKeyPair,
1943 localRegistrationId,
1944 SignalAccount.this.getIdentityKeyStore()));
1945 }
1946 }
1947
1948 public record Storage(
1949 int version,
1950 long timestamp,
1951 String serviceEnvironment,
1952 boolean registered,
1953 String number,
1954 String username,
1955 String encryptedDeviceName,
1956 int deviceId,
1957 boolean isMultiDevice,
1958 String password,
1959 AccountData aciAccountData,
1960 AccountData pniAccountData,
1961 String registrationLockPin,
1962 String pinMasterKey,
1963 String storageKey,
1964 String accountEntropyPool,
1965 String mediaRootBackupKey,
1966 String profileKey,
1967 String usernameLinkEntropy,
1968 String usernameLinkServerId
1969 ) {
1970
1971 public record AccountData(
1972 String serviceId,
1973 int registrationId,
1974 String identityPrivateKey,
1975 String identityPublicKey,
1976
1977 int nextPreKeyId,
1978 int nextSignedPreKeyId,
1979 int activeSignedPreKeyId,
1980 int nextKyberPreKeyId,
1981 int activeLastResortKyberPreKeyId
1982 ) {
1983
1984 private static AccountData from(final SignalAccount.AccountData<?> accountData) {
1985 final var base64 = Base64.getEncoder();
1986 final var preKeyMetadata = accountData.getPreKeyMetadata();
1987 return new AccountData(accountData.getServiceId() == null
1988 ? null
1989 : accountData.getServiceId().toString(),
1990 accountData.getLocalRegistrationId(),
1991 accountData.getIdentityKeyPair() == null
1992 ? null
1993 : base64.encodeToString(accountData.getIdentityKeyPair().getPrivateKey().serialize()),
1994 accountData.getIdentityKeyPair() == null
1995 ? null
1996 : base64.encodeToString(accountData.getIdentityKeyPair().getPublicKey().serialize()),
1997 preKeyMetadata.getNextPreKeyId(),
1998 preKeyMetadata.getNextSignedPreKeyId(),
1999 preKeyMetadata.getActiveSignedPreKeyId(),
2000 preKeyMetadata.getNextKyberPreKeyId(),
2001 preKeyMetadata.getActiveLastResortKyberPreKeyId());
2002 }
2003 }
2004 }
2005 }