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