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