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