]> nmode's Git Repositories - signal-cli/blob - lib/src/main/java/org/asamk/signal/manager/storage/SignalAccount.java
547607b303055799f28c4a3793c148341b809077
[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 final var aciPublicKey = getAciIdentityKeyPair().getPublicKey();
387 getIdentityKeyStore().saveIdentity(getAci(), aciPublicKey);
388 getIdentityKeyStore().setIdentityTrustLevel(getAci(), aciPublicKey, TrustLevel.TRUSTED_VERIFIED);
389 if (getPniIdentityKeyPair() != null) {
390 final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
391 getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
392 getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
393 }
394 }
395
396 private void migrateLegacyConfigs() {
397 if (getPassword() == null) {
398 setPassword(KeyUtils.createPassword());
399 }
400
401 if (getProfileKey() == null) {
402 // Old config file, creating new profile key
403 setProfileKey(KeyUtils.createProfileKey());
404 }
405 getProfileStore().storeProfileKey(getSelfRecipientId(), getProfileKey());
406 if (isPrimaryDevice() && getPniIdentityKeyPair() == null && getPni() != null) {
407 setPniIdentityKeyPair(KeyUtils.generateIdentityKeyPair());
408 }
409 }
410
411 private void mergeRecipients(RecipientId recipientId, RecipientId toBeMergedRecipientId) {
412 getMessageCache().mergeRecipients(recipientId, toBeMergedRecipientId);
413 getGroupStore().mergeRecipients(recipientId, toBeMergedRecipientId);
414 }
415
416 public void removeRecipient(final RecipientId recipientId) {
417 getRecipientStore().deleteRecipientData(recipientId);
418 getMessageCache().deleteMessages(recipientId);
419 final var recipientAddress = getRecipientStore().resolveRecipientAddress(recipientId);
420 if (recipientAddress.serviceId().isPresent()) {
421 final var serviceId = recipientAddress.serviceId().get();
422 getAciSessionStore().deleteAllSessions(serviceId);
423 getPniSessionStore().deleteAllSessions(serviceId);
424 getIdentityKeyStore().deleteIdentity(serviceId);
425 getSenderKeyStore().deleteAll(serviceId);
426 }
427 }
428
429 public static File getFileName(File dataPath, String account) {
430 return new File(dataPath, account);
431 }
432
433 private static File getUserPath(final File dataPath, final String account) {
434 final var path = new File(dataPath, account + ".d");
435 try {
436 IOUtils.createPrivateDirectories(path);
437 } catch (IOException e) {
438 throw new AssertionError("Failed to create user path", e);
439 }
440 return path;
441 }
442
443 private static File getMessageCachePath(File dataPath, String account) {
444 return new File(getUserPath(dataPath, account), "msg-cache");
445 }
446
447 private static File getGroupCachePath(File dataPath, String account) {
448 return new File(getUserPath(dataPath, account), "group-cache");
449 }
450
451 private static File getAciPreKeysPath(File dataPath, String account) {
452 return new File(getUserPath(dataPath, account), "pre-keys");
453 }
454
455 private static File getAciSignedPreKeysPath(File dataPath, String account) {
456 return new File(getUserPath(dataPath, account), "signed-pre-keys");
457 }
458
459 private static File getPniPreKeysPath(File dataPath, String account) {
460 return new File(getUserPath(dataPath, account), "pre-keys-pni");
461 }
462
463 private static File getPniSignedPreKeysPath(File dataPath, String account) {
464 return new File(getUserPath(dataPath, account), "signed-pre-keys-pni");
465 }
466
467 private static File getIdentitiesPath(File dataPath, String account) {
468 return new File(getUserPath(dataPath, account), "identities");
469 }
470
471 private static File getSessionsPath(File dataPath, String account) {
472 return new File(getUserPath(dataPath, account), "sessions");
473 }
474
475 private static File getSenderKeysPath(File dataPath, String account) {
476 return new File(getUserPath(dataPath, account), "sender-keys");
477 }
478
479 private static File getSharedSenderKeysFile(File dataPath, String account) {
480 return new File(getUserPath(dataPath, account), "shared-sender-keys-store");
481 }
482
483 private static File getRecipientsStoreFile(File dataPath, String account) {
484 return new File(getUserPath(dataPath, account), "recipients-store");
485 }
486
487 private static File getStorageManifestFile(File dataPath, String account) {
488 return new File(getUserPath(dataPath, account), "storage-manifest");
489 }
490
491 private static File getDatabaseFile(File dataPath, String account) {
492 return new File(getUserPath(dataPath, account), "account.db");
493 }
494
495 public static boolean accountFileExists(File dataPath, String account) {
496 if (account == null) {
497 return false;
498 }
499 var f = getFileName(dataPath, account);
500 return !(!f.exists() || f.isDirectory());
501 }
502
503 private void load(
504 File dataPath, String accountPath, final TrustNewIdentity trustNewIdentity
505 ) throws IOException {
506 this.dataPath = dataPath;
507 this.accountPath = accountPath;
508 final JsonNode rootNode;
509 synchronized (fileChannel) {
510 fileChannel.position(0);
511 rootNode = jsonProcessor.readTree(Channels.newInputStream(fileChannel));
512 }
513
514 var migratedLegacyConfig = false;
515
516 if (rootNode.hasNonNull("version")) {
517 var accountVersion = rootNode.get("version").asInt(1);
518 if (accountVersion > CURRENT_STORAGE_VERSION) {
519 throw new IOException("Config file was created by a more recent version: " + accountVersion);
520 } else if (accountVersion < MINIMUM_STORAGE_VERSION) {
521 throw new IOException("Config file was created by a no longer supported older version: "
522 + accountVersion);
523 }
524 previousStorageVersion = accountVersion;
525 if (accountVersion < CURRENT_STORAGE_VERSION) {
526 migratedLegacyConfig = true;
527 }
528 }
529
530 number = Utils.getNotNullNode(rootNode, "username").asText();
531 if (rootNode.hasNonNull("password")) {
532 password = rootNode.get("password").asText();
533 }
534 if (rootNode.hasNonNull("serviceEnvironment")) {
535 serviceEnvironment = ServiceEnvironment.valueOf(rootNode.get("serviceEnvironment").asText());
536 }
537 registered = Utils.getNotNullNode(rootNode, "registered").asBoolean();
538 if (rootNode.hasNonNull("uuid")) {
539 try {
540 aci = ACI.parseOrThrow(rootNode.get("uuid").asText());
541 } catch (IllegalArgumentException e) {
542 throw new IOException("Config file contains an invalid aci/uuid, needs to be a valid UUID", e);
543 }
544 }
545 if (rootNode.hasNonNull("pni")) {
546 try {
547 pni = PNI.parseOrThrow(rootNode.get("pni").asText());
548 } catch (IllegalArgumentException e) {
549 throw new IOException("Config file contains an invalid pni, needs to be a valid UUID", e);
550 }
551 }
552 if (rootNode.hasNonNull("deviceName")) {
553 encryptedDeviceName = rootNode.get("deviceName").asText();
554 }
555 if (rootNode.hasNonNull("deviceId")) {
556 deviceId = rootNode.get("deviceId").asInt();
557 }
558 if (rootNode.hasNonNull("isMultiDevice")) {
559 isMultiDevice = rootNode.get("isMultiDevice").asBoolean();
560 }
561 if (rootNode.hasNonNull("lastReceiveTimestamp")) {
562 lastReceiveTimestamp = rootNode.get("lastReceiveTimestamp").asLong();
563 }
564 int registrationId = 0;
565 if (rootNode.hasNonNull("registrationId")) {
566 registrationId = rootNode.get("registrationId").asInt();
567 }
568 if (rootNode.hasNonNull("pniRegistrationId")) {
569 localPniRegistrationId = rootNode.get("pniRegistrationId").asInt();
570 } else {
571 localPniRegistrationId = KeyHelper.generateRegistrationId(false);
572 }
573 IdentityKeyPair aciIdentityKeyPair = null;
574 if (rootNode.hasNonNull("identityPrivateKey") && rootNode.hasNonNull("identityKey")) {
575 final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("identityKey").asText());
576 final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("identityPrivateKey").asText());
577 aciIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
578 }
579 if (rootNode.hasNonNull("pniIdentityPrivateKey") && rootNode.hasNonNull("pniIdentityKey")) {
580 final var publicKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityKey").asText());
581 final var privateKeyBytes = Base64.getDecoder().decode(rootNode.get("pniIdentityPrivateKey").asText());
582 pniIdentityKeyPair = KeyUtils.getIdentityKeyPair(publicKeyBytes, privateKeyBytes);
583 }
584
585 if (rootNode.hasNonNull("registrationLockPin")) {
586 registrationLockPin = rootNode.get("registrationLockPin").asText();
587 }
588 if (rootNode.hasNonNull("pinMasterKey")) {
589 pinMasterKey = new MasterKey(Base64.getDecoder().decode(rootNode.get("pinMasterKey").asText()));
590 }
591 if (rootNode.hasNonNull("storageKey")) {
592 storageKey = new StorageKey(Base64.getDecoder().decode(rootNode.get("storageKey").asText()));
593 }
594 if (rootNode.hasNonNull("storageManifestVersion")) {
595 storageManifestVersion = rootNode.get("storageManifestVersion").asLong();
596 }
597 if (rootNode.hasNonNull("preKeyIdOffset")) {
598 aciPreKeyIdOffset = rootNode.get("preKeyIdOffset").asInt(1);
599 } else {
600 aciPreKeyIdOffset = 1;
601 }
602 if (rootNode.hasNonNull("nextSignedPreKeyId")) {
603 aciNextSignedPreKeyId = rootNode.get("nextSignedPreKeyId").asInt(1);
604 } else {
605 aciNextSignedPreKeyId = 1;
606 }
607 if (rootNode.hasNonNull("pniPreKeyIdOffset")) {
608 pniPreKeyIdOffset = rootNode.get("pniPreKeyIdOffset").asInt(1);
609 } else {
610 pniPreKeyIdOffset = 1;
611 }
612 if (rootNode.hasNonNull("pniNextSignedPreKeyId")) {
613 pniNextSignedPreKeyId = rootNode.get("pniNextSignedPreKeyId").asInt(1);
614 } else {
615 pniNextSignedPreKeyId = 1;
616 }
617 if (rootNode.hasNonNull("profileKey")) {
618 try {
619 profileKey = new ProfileKey(Base64.getDecoder().decode(rootNode.get("profileKey").asText()));
620 } catch (InvalidInputException e) {
621 throw new IOException(
622 "Config file contains an invalid profileKey, needs to be base64 encoded array of 32 bytes",
623 e);
624 }
625 }
626
627 if (previousStorageVersion < 5) {
628 final var legacyRecipientsStoreFile = getRecipientsStoreFile(dataPath, accountPath);
629 if (legacyRecipientsStoreFile.exists()) {
630 LegacyRecipientStore2.migrate(legacyRecipientsStoreFile, getRecipientStore());
631 // Ensure our profile key is stored in profile store
632 getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey());
633 migratedLegacyConfig = true;
634 }
635 }
636 final var legacyAciPreKeysPath = getAciPreKeysPath(dataPath, accountPath);
637 if (legacyAciPreKeysPath.exists()) {
638 LegacyPreKeyStore.migrate(legacyAciPreKeysPath, getAciPreKeyStore());
639 migratedLegacyConfig = true;
640 }
641 final var legacyPniPreKeysPath = getPniPreKeysPath(dataPath, accountPath);
642 if (legacyPniPreKeysPath.exists()) {
643 LegacyPreKeyStore.migrate(legacyPniPreKeysPath, getPniPreKeyStore());
644 migratedLegacyConfig = true;
645 }
646 final var legacyAciSignedPreKeysPath = getAciSignedPreKeysPath(dataPath, accountPath);
647 if (legacyAciSignedPreKeysPath.exists()) {
648 LegacySignedPreKeyStore.migrate(legacyAciSignedPreKeysPath, getAciSignedPreKeyStore());
649 migratedLegacyConfig = true;
650 }
651 final var legacyPniSignedPreKeysPath = getPniSignedPreKeysPath(dataPath, accountPath);
652 if (legacyPniSignedPreKeysPath.exists()) {
653 LegacySignedPreKeyStore.migrate(legacyPniSignedPreKeysPath, getPniSignedPreKeyStore());
654 migratedLegacyConfig = true;
655 }
656 final var legacySessionsPath = getSessionsPath(dataPath, accountPath);
657 if (legacySessionsPath.exists()) {
658 LegacySessionStore.migrate(legacySessionsPath,
659 getRecipientResolver(),
660 getRecipientAddressResolver(),
661 getAciSessionStore());
662 migratedLegacyConfig = true;
663 }
664 final var legacyIdentitiesPath = getIdentitiesPath(dataPath, accountPath);
665 if (legacyIdentitiesPath.exists()) {
666 LegacyIdentityKeyStore.migrate(legacyIdentitiesPath,
667 getRecipientResolver(),
668 getRecipientAddressResolver(),
669 getIdentityKeyStore());
670 migratedLegacyConfig = true;
671 }
672 final var legacySignalProtocolStore = rootNode.hasNonNull("axolotlStore")
673 ? jsonProcessor.convertValue(Utils.getNotNullNode(rootNode, "axolotlStore"),
674 LegacyJsonSignalProtocolStore.class)
675 : null;
676 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
677 aciIdentityKeyPair = legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentityKeyPair();
678 registrationId = legacySignalProtocolStore.getLegacyIdentityKeyStore().getLocalRegistrationId();
679 migratedLegacyConfig = true;
680 }
681
682 this.aciIdentityKeyPair = aciIdentityKeyPair;
683 this.localRegistrationId = registrationId;
684 this.trustNewIdentity = trustNewIdentity;
685
686 migratedLegacyConfig = loadLegacyStores(rootNode, legacySignalProtocolStore) || migratedLegacyConfig;
687
688 final var legacySenderKeysPath = getSenderKeysPath(dataPath, accountPath);
689 if (legacySenderKeysPath.exists()) {
690 LegacySenderKeyRecordStore.migrate(legacySenderKeysPath,
691 getRecipientResolver(),
692 getRecipientAddressResolver(),
693 getSenderKeyStore());
694 migratedLegacyConfig = true;
695 }
696 final var legacySenderKeysSharedPath = getSharedSenderKeysFile(dataPath, accountPath);
697 if (legacySenderKeysSharedPath.exists()) {
698 LegacySenderKeySharedStore.migrate(legacySenderKeysSharedPath,
699 getRecipientResolver(),
700 getRecipientAddressResolver(),
701 getSenderKeyStore());
702 migratedLegacyConfig = true;
703 }
704 if (rootNode.hasNonNull("groupStore")) {
705 final var groupStoreStorage = jsonProcessor.convertValue(rootNode.get("groupStore"),
706 LegacyGroupStore.Storage.class);
707 LegacyGroupStore.migrate(groupStoreStorage,
708 getGroupCachePath(dataPath, accountPath),
709 getRecipientResolver(),
710 getGroupStore());
711 migratedLegacyConfig = true;
712 }
713
714 if (rootNode.hasNonNull("stickerStore")) {
715 final var storage = jsonProcessor.convertValue(rootNode.get("stickerStore"),
716 LegacyStickerStore.Storage.class);
717 LegacyStickerStore.migrate(storage, getStickerStore());
718 migratedLegacyConfig = true;
719 }
720
721 if (rootNode.hasNonNull("configurationStore")) {
722 configurationStoreStorage = jsonProcessor.convertValue(rootNode.get("configurationStore"),
723 ConfigurationStore.Storage.class);
724 configurationStore = ConfigurationStore.fromStorage(configurationStoreStorage,
725 this::saveConfigurationStore);
726 } else {
727 configurationStore = new ConfigurationStore(this::saveConfigurationStore);
728 }
729
730 migratedLegacyConfig = loadLegacyThreadStore(rootNode) || migratedLegacyConfig;
731
732 if (migratedLegacyConfig) {
733 save();
734 }
735 }
736
737 private boolean loadLegacyStores(
738 final JsonNode rootNode, final LegacyJsonSignalProtocolStore legacySignalProtocolStore
739 ) {
740 var migrated = false;
741 var legacyRecipientStoreNode = rootNode.get("recipientStore");
742 if (legacyRecipientStoreNode != null) {
743 logger.debug("Migrating legacy recipient store.");
744 var legacyRecipientStore = jsonProcessor.convertValue(legacyRecipientStoreNode, LegacyRecipientStore.class);
745 if (legacyRecipientStore != null) {
746 legacyRecipientStore.getAddresses()
747 .forEach(recipient -> getRecipientStore().resolveRecipientTrusted(recipient));
748 }
749 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
750 migrated = true;
751 }
752
753 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyPreKeyStore() != null) {
754 logger.debug("Migrating legacy pre key store.");
755 for (var entry : legacySignalProtocolStore.getLegacyPreKeyStore().getPreKeys().entrySet()) {
756 try {
757 getAciPreKeyStore().storePreKey(entry.getKey(), new PreKeyRecord(entry.getValue()));
758 } catch (InvalidMessageException e) {
759 logger.warn("Failed to migrate pre key, ignoring", e);
760 }
761 }
762 migrated = true;
763 }
764
765 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacySignedPreKeyStore() != null) {
766 logger.debug("Migrating legacy signed pre key store.");
767 for (var entry : legacySignalProtocolStore.getLegacySignedPreKeyStore().getSignedPreKeys().entrySet()) {
768 try {
769 getAciSignedPreKeyStore().storeSignedPreKey(entry.getKey(),
770 new SignedPreKeyRecord(entry.getValue()));
771 } catch (InvalidMessageException e) {
772 logger.warn("Failed to migrate signed pre key, ignoring", e);
773 }
774 }
775 migrated = true;
776 }
777
778 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacySessionStore() != null) {
779 logger.debug("Migrating legacy session store.");
780 for (var session : legacySignalProtocolStore.getLegacySessionStore().getSessions()) {
781 try {
782 getAciSessionStore().storeSession(new SignalProtocolAddress(session.address.getIdentifier(),
783 session.deviceId), new SessionRecord(session.sessionRecord));
784 } catch (Exception e) {
785 logger.warn("Failed to migrate session, ignoring", e);
786 }
787 }
788 migrated = true;
789 }
790
791 if (legacySignalProtocolStore != null && legacySignalProtocolStore.getLegacyIdentityKeyStore() != null) {
792 logger.debug("Migrating legacy identity session store.");
793 for (var identity : legacySignalProtocolStore.getLegacyIdentityKeyStore().getIdentities()) {
794 if (identity.getAddress().serviceId().isEmpty()) {
795 continue;
796 }
797 final var serviceId = identity.getAddress().getServiceId();
798 getIdentityKeyStore().saveIdentity(serviceId, identity.getIdentityKey());
799 getIdentityKeyStore().setIdentityTrustLevel(serviceId,
800 identity.getIdentityKey(),
801 identity.getTrustLevel());
802 }
803 migrated = true;
804 }
805
806 if (rootNode.hasNonNull("contactStore")) {
807 logger.debug("Migrating legacy contact store.");
808 final var contactStoreNode = rootNode.get("contactStore");
809 final var contactStore = jsonProcessor.convertValue(contactStoreNode, LegacyJsonContactsStore.class);
810 for (var contact : contactStore.getContacts()) {
811 final var recipientId = getRecipientStore().resolveRecipientTrusted(contact.getAddress());
812 getContactStore().storeContact(recipientId,
813 new Contact(contact.name,
814 null,
815 contact.color,
816 contact.messageExpirationTime,
817 contact.blocked,
818 contact.archived,
819 false));
820
821 // Store profile keys only in profile store
822 var profileKeyString = contact.profileKey;
823 if (profileKeyString != null) {
824 final ProfileKey profileKey;
825 try {
826 profileKey = new ProfileKey(Base64.getDecoder().decode(profileKeyString));
827 getProfileStore().storeProfileKey(recipientId, profileKey);
828 } catch (InvalidInputException e) {
829 logger.warn("Failed to parse legacy contact profile key: {}", e.getMessage());
830 }
831 }
832 }
833 migrated = true;
834 }
835
836 if (rootNode.hasNonNull("profileStore")) {
837 logger.debug("Migrating legacy profile store.");
838 var profileStoreNode = rootNode.get("profileStore");
839 final var legacyProfileStore = jsonProcessor.convertValue(profileStoreNode, LegacyProfileStore.class);
840 for (var profileEntry : legacyProfileStore.getProfileEntries()) {
841 var recipientId = getRecipientResolver().resolveRecipient(profileEntry.getAddress());
842 // Not migrating profile key credential here, it was changed to expiring profile key credentials
843 getProfileStore().storeProfileKey(recipientId, profileEntry.getProfileKey());
844 final var profile = profileEntry.getProfile();
845 if (profile != null) {
846 final var capabilities = new HashSet<Profile.Capability>();
847 if (profile.getCapabilities() != null) {
848 if (profile.getCapabilities().gv1Migration) {
849 capabilities.add(Profile.Capability.gv1Migration);
850 }
851 if (profile.getCapabilities().storage) {
852 capabilities.add(Profile.Capability.storage);
853 }
854 }
855 final var newProfile = new Profile(profileEntry.getLastUpdateTimestamp(),
856 profile.getGivenName(),
857 profile.getFamilyName(),
858 profile.getAbout(),
859 profile.getAboutEmoji(),
860 null,
861 null,
862 profile.isUnrestrictedUnidentifiedAccess()
863 ? Profile.UnidentifiedAccessMode.UNRESTRICTED
864 : profile.getUnidentifiedAccess() != null
865 ? Profile.UnidentifiedAccessMode.ENABLED
866 : Profile.UnidentifiedAccessMode.DISABLED,
867 capabilities);
868 getProfileStore().storeProfile(recipientId, newProfile);
869 }
870 }
871 }
872
873 return migrated;
874 }
875
876 private boolean loadLegacyThreadStore(final JsonNode rootNode) {
877 var threadStoreNode = rootNode.get("threadStore");
878 if (threadStoreNode != null && !threadStoreNode.isNull()) {
879 var threadStore = jsonProcessor.convertValue(threadStoreNode, LegacyJsonThreadStore.class);
880 // Migrate thread info to group and contact store
881 for (var thread : threadStore.getThreads()) {
882 if (thread.id == null || thread.id.isEmpty()) {
883 continue;
884 }
885 try {
886 if (UuidUtil.isUuid(thread.id) || thread.id.startsWith("+")) {
887 final var recipientId = getRecipientResolver().resolveRecipient(thread.id);
888 var contact = getContactStore().getContact(recipientId);
889 if (contact != null) {
890 getContactStore().storeContact(recipientId,
891 Contact.newBuilder(contact)
892 .withMessageExpirationTime(thread.messageExpirationTime)
893 .build());
894 }
895 } else {
896 var groupInfo = getGroupStore().getGroup(GroupId.fromBase64(thread.id));
897 if (groupInfo instanceof GroupInfoV1) {
898 ((GroupInfoV1) groupInfo).messageExpirationTime = thread.messageExpirationTime;
899 getGroupStore().updateGroup(groupInfo);
900 }
901 }
902 } catch (Exception e) {
903 logger.warn("Failed to read legacy thread info: {}", e.getMessage());
904 }
905 }
906 return true;
907 }
908
909 return false;
910 }
911
912 private void saveConfigurationStore(ConfigurationStore.Storage storage) {
913 this.configurationStoreStorage = storage;
914 save();
915 }
916
917 private void save() {
918 synchronized (fileChannel) {
919 var rootNode = jsonProcessor.createObjectNode();
920 rootNode.put("version", CURRENT_STORAGE_VERSION)
921 .put("username", number)
922 .put("serviceEnvironment", serviceEnvironment == null ? null : serviceEnvironment.name())
923 .put("uuid", aci == null ? null : aci.toString())
924 .put("pni", pni == null ? null : pni.toString())
925 .put("deviceName", encryptedDeviceName)
926 .put("deviceId", deviceId)
927 .put("isMultiDevice", isMultiDevice)
928 .put("lastReceiveTimestamp", lastReceiveTimestamp)
929 .put("password", password)
930 .put("registrationId", localRegistrationId)
931 .put("pniRegistrationId", localPniRegistrationId)
932 .put("identityPrivateKey",
933 Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPrivateKey().serialize()))
934 .put("identityKey",
935 Base64.getEncoder().encodeToString(aciIdentityKeyPair.getPublicKey().serialize()))
936 .put("pniIdentityPrivateKey",
937 pniIdentityKeyPair == null
938 ? null
939 : Base64.getEncoder()
940 .encodeToString(pniIdentityKeyPair.getPrivateKey().serialize()))
941 .put("pniIdentityKey",
942 pniIdentityKeyPair == null
943 ? null
944 : Base64.getEncoder().encodeToString(pniIdentityKeyPair.getPublicKey().serialize()))
945 .put("registrationLockPin", registrationLockPin)
946 .put("pinMasterKey",
947 pinMasterKey == null ? null : Base64.getEncoder().encodeToString(pinMasterKey.serialize()))
948 .put("storageKey",
949 storageKey == null ? null : Base64.getEncoder().encodeToString(storageKey.serialize()))
950 .put("storageManifestVersion", storageManifestVersion == -1 ? null : storageManifestVersion)
951 .put("preKeyIdOffset", aciPreKeyIdOffset)
952 .put("nextSignedPreKeyId", aciNextSignedPreKeyId)
953 .put("pniPreKeyIdOffset", pniPreKeyIdOffset)
954 .put("pniNextSignedPreKeyId", pniNextSignedPreKeyId)
955 .put("profileKey",
956 profileKey == null ? null : Base64.getEncoder().encodeToString(profileKey.serialize()))
957 .put("registered", registered)
958 .putPOJO("configurationStore", configurationStoreStorage);
959 try {
960 try (var output = new ByteArrayOutputStream()) {
961 // Write to memory first to prevent corrupting the file in case of serialization errors
962 jsonProcessor.writeValue(output, rootNode);
963 var input = new ByteArrayInputStream(output.toByteArray());
964 fileChannel.position(0);
965 input.transferTo(Channels.newOutputStream(fileChannel));
966 fileChannel.truncate(fileChannel.position());
967 fileChannel.force(false);
968 }
969 } catch (Exception e) {
970 logger.error("Error saving file: {}", e.getMessage(), e);
971 }
972 }
973 }
974
975 private static Pair<FileChannel, FileLock> openFileChannel(File fileName, boolean waitForLock) throws IOException {
976 var fileChannel = new RandomAccessFile(fileName, "rw").getChannel();
977 try {
978 var lock = fileChannel.tryLock();
979 if (lock == null) {
980 if (!waitForLock) {
981 logger.debug("Config file is in use by another instance.");
982 throw new IOException("Config file is in use by another instance.");
983 }
984 logger.info("Config file is in use by another instance, waiting…");
985 lock = fileChannel.lock();
986 logger.info("Config file lock acquired.");
987 }
988 final var result = new Pair<>(fileChannel, lock);
989 fileChannel = null;
990 return result;
991 } finally {
992 if (fileChannel != null) {
993 fileChannel.close();
994 }
995 }
996 }
997
998 public void addPreKeys(ServiceIdType serviceIdType, List<PreKeyRecord> records) {
999 if (serviceIdType.equals(ServiceIdType.ACI)) {
1000 addAciPreKeys(records);
1001 } else {
1002 addPniPreKeys(records);
1003 }
1004 }
1005
1006 public void addAciPreKeys(List<PreKeyRecord> records) {
1007 for (var record : records) {
1008 if (aciPreKeyIdOffset != record.getId()) {
1009 logger.error("Invalid pre key id {}, expected {}", record.getId(), aciPreKeyIdOffset);
1010 throw new AssertionError("Invalid pre key id");
1011 }
1012 getAciPreKeyStore().storePreKey(record.getId(), record);
1013 aciPreKeyIdOffset = (aciPreKeyIdOffset + 1) % Medium.MAX_VALUE;
1014 }
1015 save();
1016 }
1017
1018 public void addPniPreKeys(List<PreKeyRecord> records) {
1019 for (var record : records) {
1020 if (pniPreKeyIdOffset != record.getId()) {
1021 logger.error("Invalid pre key id {}, expected {}", record.getId(), pniPreKeyIdOffset);
1022 throw new AssertionError("Invalid pre key id");
1023 }
1024 getPniPreKeyStore().storePreKey(record.getId(), record);
1025 pniPreKeyIdOffset = (pniPreKeyIdOffset + 1) % Medium.MAX_VALUE;
1026 }
1027 save();
1028 }
1029
1030 public void addSignedPreKey(ServiceIdType serviceIdType, SignedPreKeyRecord record) {
1031 if (serviceIdType.equals(ServiceIdType.ACI)) {
1032 addAciSignedPreKey(record);
1033 } else {
1034 addPniSignedPreKey(record);
1035 }
1036 }
1037
1038 public void addAciSignedPreKey(SignedPreKeyRecord record) {
1039 if (aciNextSignedPreKeyId != record.getId()) {
1040 logger.error("Invalid signed pre key id {}, expected {}", record.getId(), aciNextSignedPreKeyId);
1041 throw new AssertionError("Invalid signed pre key id");
1042 }
1043 getAciSignedPreKeyStore().storeSignedPreKey(record.getId(), record);
1044 aciNextSignedPreKeyId = (aciNextSignedPreKeyId + 1) % Medium.MAX_VALUE;
1045 save();
1046 }
1047
1048 public void addPniSignedPreKey(SignedPreKeyRecord record) {
1049 if (pniNextSignedPreKeyId != record.getId()) {
1050 logger.error("Invalid signed pre key id {}, expected {}", record.getId(), pniNextSignedPreKeyId);
1051 throw new AssertionError("Invalid signed pre key id");
1052 }
1053 getPniSignedPreKeyStore().storeSignedPreKey(record.getId(), record);
1054 pniNextSignedPreKeyId = (pniNextSignedPreKeyId + 1) % Medium.MAX_VALUE;
1055 save();
1056 }
1057
1058 public int getPreviousStorageVersion() {
1059 return previousStorageVersion;
1060 }
1061
1062 public SignalServiceDataStore getSignalServiceDataStore() {
1063 return new SignalServiceDataStore() {
1064 @Override
1065 public SignalServiceAccountDataStore get(final ServiceId accountIdentifier) {
1066 if (accountIdentifier.equals(aci)) {
1067 return aci();
1068 } else if (accountIdentifier.equals(pni)) {
1069 return pni();
1070 } else {
1071 throw new IllegalArgumentException("No matching store found for " + accountIdentifier);
1072 }
1073 }
1074
1075 @Override
1076 public SignalServiceAccountDataStore aci() {
1077 return getAciSignalServiceAccountDataStore();
1078 }
1079
1080 @Override
1081 public SignalServiceAccountDataStore pni() {
1082 return getPniSignalServiceAccountDataStore();
1083 }
1084
1085 @Override
1086 public boolean isMultiDevice() {
1087 return SignalAccount.this.isMultiDevice();
1088 }
1089 };
1090 }
1091
1092 private SignalServiceAccountDataStore getAciSignalServiceAccountDataStore() {
1093 return getOrCreate(() -> aciSignalProtocolStore,
1094 () -> aciSignalProtocolStore = new SignalProtocolStore(getAciPreKeyStore(),
1095 getAciSignedPreKeyStore(),
1096 getAciSessionStore(),
1097 getAciIdentityKeyStore(),
1098 getSenderKeyStore(),
1099 this::isMultiDevice));
1100 }
1101
1102 private SignalServiceAccountDataStore getPniSignalServiceAccountDataStore() {
1103 return getOrCreate(() -> pniSignalProtocolStore,
1104 () -> pniSignalProtocolStore = new SignalProtocolStore(getPniPreKeyStore(),
1105 getPniSignedPreKeyStore(),
1106 getPniSessionStore(),
1107 getPniIdentityKeyStore(),
1108 getSenderKeyStore(),
1109 this::isMultiDevice));
1110 }
1111
1112 private PreKeyStore getAciPreKeyStore() {
1113 return getOrCreate(() -> aciPreKeyStore,
1114 () -> aciPreKeyStore = new PreKeyStore(getAccountDatabase(), ServiceIdType.ACI));
1115 }
1116
1117 private SignedPreKeyStore getAciSignedPreKeyStore() {
1118 return getOrCreate(() -> aciSignedPreKeyStore,
1119 () -> aciSignedPreKeyStore = new SignedPreKeyStore(getAccountDatabase(), ServiceIdType.ACI));
1120 }
1121
1122 private PreKeyStore getPniPreKeyStore() {
1123 return getOrCreate(() -> pniPreKeyStore,
1124 () -> pniPreKeyStore = new PreKeyStore(getAccountDatabase(), ServiceIdType.PNI));
1125 }
1126
1127 private SignedPreKeyStore getPniSignedPreKeyStore() {
1128 return getOrCreate(() -> pniSignedPreKeyStore,
1129 () -> pniSignedPreKeyStore = new SignedPreKeyStore(getAccountDatabase(), ServiceIdType.PNI));
1130 }
1131
1132 public SessionStore getAciSessionStore() {
1133 return getOrCreate(() -> aciSessionStore,
1134 () -> aciSessionStore = new SessionStore(getAccountDatabase(), ServiceIdType.ACI));
1135 }
1136
1137 public SessionStore getPniSessionStore() {
1138 return getOrCreate(() -> pniSessionStore,
1139 () -> pniSessionStore = new SessionStore(getAccountDatabase(), ServiceIdType.PNI));
1140 }
1141
1142 public IdentityKeyStore getIdentityKeyStore() {
1143 return getOrCreate(() -> identityKeyStore,
1144 () -> identityKeyStore = new IdentityKeyStore(getAccountDatabase(), trustNewIdentity));
1145 }
1146
1147 public SignalIdentityKeyStore getAciIdentityKeyStore() {
1148 return getOrCreate(() -> aciIdentityKeyStore,
1149 () -> aciIdentityKeyStore = new SignalIdentityKeyStore(getRecipientResolver(),
1150 () -> aciIdentityKeyPair,
1151 localRegistrationId,
1152 getIdentityKeyStore()));
1153 }
1154
1155 public SignalIdentityKeyStore getPniIdentityKeyStore() {
1156 return getOrCreate(() -> pniIdentityKeyStore,
1157 () -> pniIdentityKeyStore = new SignalIdentityKeyStore(getRecipientResolver(),
1158 () -> pniIdentityKeyPair,
1159 localRegistrationId,
1160 getIdentityKeyStore()));
1161 }
1162
1163 public GroupStore getGroupStore() {
1164 return getOrCreate(() -> groupStore,
1165 () -> groupStore = new GroupStore(getAccountDatabase(),
1166 getRecipientResolver(),
1167 getRecipientIdCreator()));
1168 }
1169
1170 public ContactsStore getContactStore() {
1171 return getRecipientStore();
1172 }
1173
1174 private RecipientIdCreator getRecipientIdCreator() {
1175 return recipientId -> getRecipientStore().create(recipientId);
1176 }
1177
1178 public RecipientResolver getRecipientResolver() {
1179 return new RecipientResolver() {
1180 @Override
1181 public RecipientId resolveRecipient(final RecipientAddress address) {
1182 return getRecipientStore().resolveRecipient(address);
1183 }
1184
1185 @Override
1186 public RecipientId resolveRecipient(final long recipientId) {
1187 return getRecipientStore().resolveRecipient(recipientId);
1188 }
1189 };
1190 }
1191
1192 public RecipientTrustedResolver getRecipientTrustedResolver() {
1193 return new RecipientTrustedResolver() {
1194 @Override
1195 public RecipientId resolveSelfRecipientTrusted(final RecipientAddress address) {
1196 return getRecipientStore().resolveSelfRecipientTrusted(address);
1197 }
1198
1199 @Override
1200 public RecipientId resolveRecipientTrusted(final SignalServiceAddress address) {
1201 return getRecipientStore().resolveRecipientTrusted(address);
1202 }
1203
1204 @Override
1205 public RecipientId resolveRecipientTrusted(
1206 final Optional<ACI> aci, final Optional<PNI> pni, final Optional<String> number
1207 ) {
1208 return getRecipientStore().resolveRecipientTrusted(aci, pni, number);
1209 }
1210 };
1211 }
1212
1213 public RecipientAddressResolver getRecipientAddressResolver() {
1214 return recipientId -> getRecipientStore().resolveRecipientAddress(recipientId);
1215 }
1216
1217 public RecipientStore getRecipientStore() {
1218 return getOrCreate(() -> recipientStore,
1219 () -> recipientStore = new RecipientStore(this::mergeRecipients,
1220 this::getSelfRecipientAddress,
1221 getAccountDatabase()));
1222 }
1223
1224 public ProfileStore getProfileStore() {
1225 return getRecipientStore();
1226 }
1227
1228 public StickerStore getStickerStore() {
1229 return getOrCreate(() -> stickerStore, () -> stickerStore = new StickerStore(getAccountDatabase()));
1230 }
1231
1232 public SenderKeyStore getSenderKeyStore() {
1233 return getOrCreate(() -> senderKeyStore, () -> senderKeyStore = new SenderKeyStore(getAccountDatabase()));
1234 }
1235
1236 public ConfigurationStore getConfigurationStore() {
1237 return configurationStore;
1238 }
1239
1240 public MessageCache getMessageCache() {
1241 return getOrCreate(() -> messageCache,
1242 () -> messageCache = new MessageCache(getMessageCachePath(dataPath, accountPath)));
1243 }
1244
1245 public AccountDatabase getAccountDatabase() {
1246 return getOrCreate(() -> accountDatabase, () -> {
1247 try {
1248 accountDatabase = AccountDatabase.init(getDatabaseFile(dataPath, accountPath));
1249 } catch (SQLException e) {
1250 throw new RuntimeException(e);
1251 }
1252 });
1253 }
1254
1255 public MessageSendLogStore getMessageSendLogStore() {
1256 return getOrCreate(() -> messageSendLogStore,
1257 () -> messageSendLogStore = new MessageSendLogStore(getAccountDatabase()));
1258 }
1259
1260 public CredentialsProvider getCredentialsProvider() {
1261 return new CredentialsProvider() {
1262 @Override
1263 public ACI getAci() {
1264 return aci;
1265 }
1266
1267 @Override
1268 public PNI getPni() {
1269 return pni;
1270 }
1271
1272 @Override
1273 public String getE164() {
1274 return number;
1275 }
1276
1277 @Override
1278 public String getPassword() {
1279 return password;
1280 }
1281
1282 @Override
1283 public int getDeviceId() {
1284 return deviceId;
1285 }
1286 };
1287 }
1288
1289 public String getNumber() {
1290 return number;
1291 }
1292
1293 public void setNumber(final String number) {
1294 this.number = number;
1295 save();
1296 }
1297
1298 public ServiceEnvironment getServiceEnvironment() {
1299 return serviceEnvironment;
1300 }
1301
1302 public void setServiceEnvironment(final ServiceEnvironment serviceEnvironment) {
1303 this.serviceEnvironment = serviceEnvironment;
1304 save();
1305 }
1306
1307 public ServiceId getAccountId(ServiceIdType serviceIdType) {
1308 return serviceIdType.equals(ServiceIdType.ACI) ? aci : pni;
1309 }
1310
1311 public ACI getAci() {
1312 return aci;
1313 }
1314
1315 public void setAci(final ACI aci) {
1316 this.aci = aci;
1317 save();
1318 }
1319
1320 public PNI getPni() {
1321 return pni;
1322 }
1323
1324 public void setPni(final PNI pni) {
1325 this.pni = pni;
1326 save();
1327 }
1328
1329 public SignalServiceAddress getSelfAddress() {
1330 return new SignalServiceAddress(aci, number);
1331 }
1332
1333 public RecipientAddress getSelfRecipientAddress() {
1334 return new RecipientAddress(aci, pni, number);
1335 }
1336
1337 public RecipientId getSelfRecipientId() {
1338 return getRecipientResolver().resolveRecipient(getSelfRecipientAddress());
1339 }
1340
1341 public byte[] getEncryptedDeviceName() {
1342 return encryptedDeviceName == null ? null : Base64.getDecoder().decode(encryptedDeviceName);
1343 }
1344
1345 public void setEncryptedDeviceName(final String encryptedDeviceName) {
1346 this.encryptedDeviceName = encryptedDeviceName;
1347 save();
1348 }
1349
1350 public int getDeviceId() {
1351 return deviceId;
1352 }
1353
1354 public boolean isPrimaryDevice() {
1355 return deviceId == SignalServiceAddress.DEFAULT_DEVICE_ID;
1356 }
1357
1358 public IdentityKeyPair getIdentityKeyPair(ServiceIdType serviceIdType) {
1359 return serviceIdType.equals(ServiceIdType.ACI) ? aciIdentityKeyPair : pniIdentityKeyPair;
1360 }
1361
1362 public IdentityKeyPair getAciIdentityKeyPair() {
1363 return aciIdentityKeyPair;
1364 }
1365
1366 public IdentityKeyPair getPniIdentityKeyPair() {
1367 return pniIdentityKeyPair;
1368 }
1369
1370 public void setPniIdentityKeyPair(final IdentityKeyPair identityKeyPair) {
1371 pniIdentityKeyPair = identityKeyPair;
1372 final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
1373 getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
1374 getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
1375 save();
1376 }
1377
1378 public int getLocalRegistrationId() {
1379 return localRegistrationId;
1380 }
1381
1382 public int getLocalPniRegistrationId() {
1383 return localPniRegistrationId;
1384 }
1385
1386 public String getPassword() {
1387 return password;
1388 }
1389
1390 private void setPassword(final String password) {
1391 this.password = password;
1392 save();
1393 }
1394
1395 public void setRegistrationLockPin(final String registrationLockPin) {
1396 this.registrationLockPin = registrationLockPin;
1397 save();
1398 }
1399
1400 public String getRegistrationLockPin() {
1401 return registrationLockPin;
1402 }
1403
1404 public String getRegistrationLock() {
1405 final var masterKey = getPinBackedMasterKey();
1406 if (masterKey == null) {
1407 return null;
1408 }
1409 return masterKey.deriveRegistrationLock();
1410 }
1411
1412 public MasterKey getPinBackedMasterKey() {
1413 if (registrationLockPin == null) {
1414 return null;
1415 }
1416 return pinMasterKey;
1417 }
1418
1419 public MasterKey getOrCreatePinMasterKey() {
1420 if (pinMasterKey == null) {
1421 pinMasterKey = KeyUtils.createMasterKey();
1422 save();
1423 }
1424 return pinMasterKey;
1425 }
1426
1427 public StorageKey getStorageKey() {
1428 if (pinMasterKey != null) {
1429 return pinMasterKey.deriveStorageServiceKey();
1430 }
1431 return storageKey;
1432 }
1433
1434 public StorageKey getOrCreateStorageKey() {
1435 if (isPrimaryDevice()) {
1436 return getOrCreatePinMasterKey().deriveStorageServiceKey();
1437 }
1438 return storageKey;
1439 }
1440
1441 public void setStorageKey(final StorageKey storageKey) {
1442 if (storageKey.equals(this.storageKey)) {
1443 return;
1444 }
1445 this.storageKey = storageKey;
1446 save();
1447 }
1448
1449 public long getStorageManifestVersion() {
1450 return this.storageManifestVersion;
1451 }
1452
1453 public void setStorageManifestVersion(final long storageManifestVersion) {
1454 if (storageManifestVersion == this.storageManifestVersion) {
1455 return;
1456 }
1457 this.storageManifestVersion = storageManifestVersion;
1458 save();
1459 }
1460
1461 public Optional<SignalStorageManifest> getStorageManifest() {
1462 final var storageManifestFile = getStorageManifestFile(dataPath, accountPath);
1463 if (!storageManifestFile.exists()) {
1464 return Optional.empty();
1465 }
1466 try (var inputStream = new FileInputStream(storageManifestFile)) {
1467 return Optional.of(SignalStorageManifest.deserialize(inputStream.readAllBytes()));
1468 } catch (IOException e) {
1469 logger.warn("Failed to read local storage manifest.", e);
1470 return Optional.empty();
1471 }
1472 }
1473
1474 public void setStorageManifest(SignalStorageManifest manifest) {
1475 final var storageManifestFile = getStorageManifestFile(dataPath, accountPath);
1476 if (manifest == null) {
1477 if (storageManifestFile.exists()) {
1478 try {
1479 Files.delete(storageManifestFile.toPath());
1480 } catch (IOException e) {
1481 logger.error("Failed to delete local storage manifest.", e);
1482 }
1483 }
1484 return;
1485 }
1486
1487 final var manifestBytes = manifest.serialize();
1488 try (var outputStream = new FileOutputStream(storageManifestFile)) {
1489 outputStream.write(manifestBytes);
1490 } catch (IOException e) {
1491 logger.error("Failed to store local storage manifest.", e);
1492 }
1493 }
1494
1495 public ProfileKey getProfileKey() {
1496 return profileKey;
1497 }
1498
1499 public void setProfileKey(final ProfileKey profileKey) {
1500 if (profileKey.equals(this.profileKey)) {
1501 return;
1502 }
1503 this.profileKey = profileKey;
1504 save();
1505 getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey());
1506 }
1507
1508 public byte[] getSelfUnidentifiedAccessKey() {
1509 return UnidentifiedAccess.deriveAccessKeyFrom(getProfileKey());
1510 }
1511
1512 public int getPreKeyIdOffset(ServiceIdType serviceIdType) {
1513 return serviceIdType.equals(ServiceIdType.ACI) ? aciPreKeyIdOffset : pniPreKeyIdOffset;
1514 }
1515
1516 public int getNextSignedPreKeyId(ServiceIdType serviceIdType) {
1517 return serviceIdType.equals(ServiceIdType.ACI) ? aciNextSignedPreKeyId : pniNextSignedPreKeyId;
1518 }
1519
1520 public boolean isRegistered() {
1521 return registered;
1522 }
1523
1524 public void setRegistered(final boolean registered) {
1525 this.registered = registered;
1526 save();
1527 }
1528
1529 public boolean isMultiDevice() {
1530 return isMultiDevice;
1531 }
1532
1533 public void setMultiDevice(final boolean multiDevice) {
1534 if (isMultiDevice == multiDevice) {
1535 return;
1536 }
1537 isMultiDevice = multiDevice;
1538 save();
1539 }
1540
1541 public long getLastReceiveTimestamp() {
1542 return lastReceiveTimestamp;
1543 }
1544
1545 public void setLastReceiveTimestamp(final long lastReceiveTimestamp) {
1546 this.lastReceiveTimestamp = lastReceiveTimestamp;
1547 save();
1548 }
1549
1550 public boolean isUnrestrictedUnidentifiedAccess() {
1551 final var profile = getProfileStore().getProfile(getSelfRecipientId());
1552 return profile != null && profile.getUnidentifiedAccessMode() == Profile.UnidentifiedAccessMode.UNRESTRICTED;
1553 }
1554
1555 public boolean isDiscoverableByPhoneNumber() {
1556 final var phoneNumberUnlisted = configurationStore.getPhoneNumberUnlisted();
1557 return phoneNumberUnlisted == null || !phoneNumberUnlisted;
1558 }
1559
1560 public void finishRegistration(final ACI aci, final PNI pni, final MasterKey masterKey, final String pin) {
1561 this.pinMasterKey = masterKey;
1562 this.storageManifestVersion = -1;
1563 this.setStorageManifest(null);
1564 this.storageKey = null;
1565 this.encryptedDeviceName = null;
1566 this.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
1567 this.isMultiDevice = false;
1568 this.registered = true;
1569 this.aci = aci;
1570 this.pni = pni;
1571 this.registrationLockPin = pin;
1572 this.lastReceiveTimestamp = 0;
1573 save();
1574
1575 clearAllPreKeys();
1576 getAciSessionStore().archiveAllSessions();
1577 getPniSessionStore().archiveAllSessions();
1578 getSenderKeyStore().deleteAll();
1579 getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
1580 final var aciPublicKey = getAciIdentityKeyPair().getPublicKey();
1581 getIdentityKeyStore().saveIdentity(getAci(), aciPublicKey);
1582 getIdentityKeyStore().setIdentityTrustLevel(getAci(), aciPublicKey, TrustLevel.TRUSTED_VERIFIED);
1583 final var pniPublicKey = getPniIdentityKeyPair().getPublicKey();
1584 getIdentityKeyStore().saveIdentity(getPni(), pniPublicKey);
1585 getIdentityKeyStore().setIdentityTrustLevel(getPni(), pniPublicKey, TrustLevel.TRUSTED_VERIFIED);
1586 }
1587
1588 public void deleteAccountData() throws IOException {
1589 close();
1590 try (final var files = Files.walk(getUserPath(dataPath, accountPath).toPath())
1591 .sorted(Comparator.reverseOrder())) {
1592 for (final var file = files.iterator(); file.hasNext(); ) {
1593 Files.delete(file.next());
1594 }
1595 }
1596 Files.delete(getFileName(dataPath, accountPath).toPath());
1597 }
1598
1599 @Override
1600 public void close() {
1601 synchronized (fileChannel) {
1602 if (accountDatabase != null) {
1603 try {
1604 accountDatabase.close();
1605 } catch (SQLException e) {
1606 logger.warn("Failed to close account database: {}", e.getMessage(), e);
1607 }
1608 }
1609 if (messageSendLogStore != null) {
1610 messageSendLogStore.close();
1611 }
1612 try {
1613 try {
1614 lock.close();
1615 } catch (ClosedChannelException ignored) {
1616 }
1617 fileChannel.close();
1618 } catch (IOException e) {
1619 logger.warn("Failed to close account: {}", e.getMessage(), e);
1620 }
1621 }
1622 }
1623
1624 private <T> T getOrCreate(Supplier<T> supplier, Callable creator) {
1625 var value = supplier.get();
1626 if (value != null) {
1627 return value;
1628 }
1629
1630 synchronized (LOCK) {
1631 value = supplier.get();
1632 if (value != null) {
1633 return value;
1634 }
1635 creator.call();
1636 return supplier.get();
1637 }
1638 }
1639
1640 private interface Callable {
1641
1642 void call();
1643 }
1644 }