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