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