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