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