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