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