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