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