Refactor creating linked account files

This commit is contained in:
AsamK 2023-10-16 18:28:08 +02:00
parent dd3326f038
commit 400dcf2899
3 changed files with 64 additions and 140 deletions

View file

@ -14,7 +14,6 @@ import org.asamk.signal.manager.internal.RegistrationManagerImpl;
import org.asamk.signal.manager.storage.SignalAccount; import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.accounts.AccountsStore;
import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.KeyUtils;
import org.signal.libsignal.protocol.util.KeyHelper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException; import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
@ -160,8 +159,6 @@ public class SignalAccountFiles {
final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath; final var newAccountPath = accountPath == null ? accountsStore.addAccount(number, null) : accountPath;
var aciIdentityKey = KeyUtils.generateIdentityKeyPair(); var aciIdentityKey = KeyUtils.generateIdentityKeyPair();
var pniIdentityKey = KeyUtils.generateIdentityKeyPair(); var pniIdentityKey = KeyUtils.generateIdentityKeyPair();
var registrationId = KeyHelper.generateRegistrationId(false);
var pniRegistrationId = KeyHelper.generateRegistrationId(false);
var profileKey = KeyUtils.createProfileKey(); var profileKey = KeyUtils.createProfileKey();
var account = SignalAccount.create(pathConfig.dataPath(), var account = SignalAccount.create(pathConfig.dataPath(),
@ -170,8 +167,6 @@ public class SignalAccountFiles {
serviceEnvironment, serviceEnvironment,
aciIdentityKey, aciIdentityKey,
pniIdentityKey, pniIdentityKey,
registrationId,
pniRegistrationId,
profileKey, profileKey,
settings); settings);

View file

@ -27,12 +27,12 @@ import org.asamk.signal.manager.storage.SignalAccount;
import org.asamk.signal.manager.storage.accounts.AccountsStore; import org.asamk.signal.manager.storage.accounts.AccountsStore;
import org.asamk.signal.manager.util.KeyUtils; import org.asamk.signal.manager.util.KeyUtils;
import org.signal.libsignal.protocol.IdentityKeyPair; import org.signal.libsignal.protocol.IdentityKeyPair;
import org.signal.libsignal.protocol.util.KeyHelper;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.SignalServiceAccountManager; import org.whispersystems.signalservice.api.SignalServiceAccountManager;
import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations; import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException; import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.util.DeviceNameUtil; import org.whispersystems.signalservice.api.util.DeviceNameUtil;
@ -59,8 +59,6 @@ public class ProvisioningManagerImpl implements ProvisioningManager {
private final SignalServiceAccountManager accountManager; private final SignalServiceAccountManager accountManager;
private final IdentityKeyPair tempIdentityKey; private final IdentityKeyPair tempIdentityKey;
private final int registrationId;
private final int pniRegistrationId;
private final String password; private final String password;
public ProvisioningManagerImpl( public ProvisioningManagerImpl(
@ -77,8 +75,6 @@ public class ProvisioningManagerImpl implements ProvisioningManager {
this.accountsStore = accountsStore; this.accountsStore = accountsStore;
tempIdentityKey = KeyUtils.generateIdentityKeyPair(); tempIdentityKey = KeyUtils.generateIdentityKeyPair();
registrationId = KeyHelper.generateRegistrationId(false);
pniRegistrationId = KeyHelper.generateRegistrationId(false);
password = KeyUtils.createPassword(); password = KeyUtils.createPassword();
GroupsV2Operations groupsV2Operations; GroupsV2Operations groupsV2Operations;
try { try {
@ -114,9 +110,9 @@ public class ProvisioningManagerImpl implements ProvisioningManager {
if (accountPath == null) { if (accountPath == null) {
accountPath = accountsStore.getPathByNumber(number); accountPath = accountsStore.getPathByNumber(number);
} }
if (accountPath != null final var accountExists = accountPath != null && SignalAccount.accountFileExists(pathConfig.dataPath(),
&& SignalAccount.accountFileExists(pathConfig.dataPath(), accountPath) accountPath);
&& !canRelinkExistingAccount(accountPath)) { if (accountExists && !canRelinkExistingAccount(accountPath)) {
throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), accountPath)); throw new UserAlreadyExistsException(number, SignalAccount.getFileName(pathConfig.dataPath(), accountPath));
} }
if (accountPath == null) { if (accountPath == null) {
@ -128,38 +124,42 @@ public class ProvisioningManagerImpl implements ProvisioningManager {
var encryptedDeviceName = deviceName == null var encryptedDeviceName = deviceName == null
? null ? null
: DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey()); : DeviceNameUtil.encryptDeviceName(deviceName, ret.getAciIdentity().getPrivateKey());
logger.debug("Finishing new device registration");
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
new ConfirmCodeMessage(false,
true,
registrationId,
pniRegistrationId,
encryptedDeviceName,
getCapabilities(false)));
// Create new account with the synced identity // Create new account with the synced identity
var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey(); var profileKey = ret.getProfileKey() == null ? KeyUtils.createProfileKey() : ret.getProfileKey();
SignalAccount account = null; SignalAccount account = null;
try { try {
account = SignalAccount.createOrUpdateLinkedAccount(pathConfig.dataPath(), if (!accountExists) {
account = SignalAccount.createLinkedAccount(pathConfig.dataPath(),
accountPath, accountPath,
number,
serviceEnvironmentConfig.type(), serviceEnvironmentConfig.type(),
Settings.DEFAULT);
} else {
account = SignalAccount.load(pathConfig.dataPath(), accountPath, true, Settings.DEFAULT);
}
account.setProvisioningData(number,
aci, aci,
pni, pni,
password, password,
encryptedDeviceName, encryptedDeviceName,
deviceId,
ret.getAciIdentity(), ret.getAciIdentity(),
ret.getPniIdentity(), ret.getPniIdentity(),
registrationId, profileKey);
pniRegistrationId,
profileKey,
Settings.DEFAULT);
account.getConfigurationStore().setReadReceipts(ret.isReadReceipts()); account.getConfigurationStore().setReadReceipts(ret.isReadReceipts());
logger.debug("Finishing new device registration");
var deviceId = accountManager.finishNewDeviceRegistration(ret.getProvisioningCode(),
new ConfirmCodeMessage(false,
true,
account.getAccountData(ServiceIdType.ACI).getLocalRegistrationId(),
account.getAccountData(ServiceIdType.PNI).getLocalRegistrationId(),
encryptedDeviceName,
getCapabilities(false)));
account.finishLinking(deviceId);
ManagerImpl m = null; ManagerImpl m = null;
try { try {
m = new ManagerImpl(account, m = new ManagerImpl(account,

View file

@ -130,7 +130,7 @@ public class SignalAccount implements Closeable {
private String number; private String number;
private String username; private String username;
private String encryptedDeviceName; private String encryptedDeviceName;
private int deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID; private int deviceId = 0;
private String password; private String password;
private String registrationLockPin; private String registrationLockPin;
private MasterKey pinMasterKey; private MasterKey pinMasterKey;
@ -204,8 +204,6 @@ public class SignalAccount implements Closeable {
ServiceEnvironment serviceEnvironment, ServiceEnvironment serviceEnvironment,
IdentityKeyPair aciIdentityKey, IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey, IdentityKeyPair pniIdentityKey,
int registrationId,
int pniRegistrationId,
ProfileKey profileKey, ProfileKey profileKey,
final Settings settings final Settings settings
) throws IOException { ) throws IOException {
@ -223,160 +221,91 @@ public class SignalAccount implements Closeable {
signalAccount.serviceEnvironment = serviceEnvironment; signalAccount.serviceEnvironment = serviceEnvironment;
signalAccount.profileKey = profileKey; signalAccount.profileKey = profileKey;
signalAccount.password = KeyUtils.createPassword(); signalAccount.password = KeyUtils.createPassword();
signalAccount.deviceId = SignalServiceAddress.DEFAULT_DEVICE_ID;
signalAccount.dataPath = dataPath; signalAccount.dataPath = dataPath;
signalAccount.aciAccountData.setIdentityKeyPair(aciIdentityKey); signalAccount.aciAccountData.setIdentityKeyPair(aciIdentityKey);
signalAccount.pniAccountData.setIdentityKeyPair(pniIdentityKey); signalAccount.pniAccountData.setIdentityKeyPair(pniIdentityKey);
signalAccount.aciAccountData.setLocalRegistrationId(registrationId); signalAccount.aciAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
signalAccount.pniAccountData.setLocalRegistrationId(pniRegistrationId); signalAccount.pniAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
signalAccount.settings = settings; signalAccount.settings = settings;
signalAccount.registered = false; signalAccount.registered = false;
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION; signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs(); signalAccount.migrateLegacyConfigs();
signalAccount.clearAllPreKeys();
signalAccount.save(); signalAccount.save();
return signalAccount; return signalAccount;
} }
private static SignalAccount createLinkedAccount( public static SignalAccount createLinkedAccount(
File dataPath, final File dataPath,
String accountPath, final String accountPath,
String number, final ServiceEnvironment serviceEnvironment,
ServiceEnvironment serviceEnvironment,
ACI aci,
PNI pni,
String password,
String encryptedDeviceName,
int deviceId,
IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId,
int pniRegistrationId,
ProfileKey profileKey,
final Settings settings
) throws IOException {
var fileName = getFileName(dataPath, accountPath);
IOUtils.createPrivateFile(fileName);
final var pair = openFileChannel(fileName, true);
var signalAccount = new SignalAccount(pair.first(), pair.second());
signalAccount.dataPath = dataPath;
signalAccount.accountPath = accountPath;
signalAccount.serviceEnvironment = serviceEnvironment;
signalAccount.aciAccountData.setLocalRegistrationId(registrationId);
signalAccount.pniAccountData.setLocalRegistrationId(pniRegistrationId);
signalAccount.settings = settings;
signalAccount.setProvisioningData(number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
aciIdentityKey,
pniIdentityKey,
profileKey);
signalAccount.getRecipientTrustedResolver()
.resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
signalAccount.migrateLegacyConfigs();
signalAccount.clearAllPreKeys();
signalAccount.save();
return signalAccount;
}
public static SignalAccount createOrUpdateLinkedAccount(
File dataPath,
String accountPath,
String number,
ServiceEnvironment serviceEnvironment,
ACI aci,
PNI pni,
String password,
String encryptedDeviceName,
int deviceId,
IdentityKeyPair aciIdentityKey,
IdentityKeyPair pniIdentityKey,
int registrationId,
int pniRegistrationId,
ProfileKey profileKey,
final Settings settings final Settings settings
) throws IOException { ) throws IOException {
IOUtils.createPrivateDirectories(dataPath); IOUtils.createPrivateDirectories(dataPath);
var fileName = getFileName(dataPath, accountPath); var fileName = getFileName(dataPath, accountPath);
if (!fileName.exists()) { IOUtils.createPrivateFile(fileName);
return createLinkedAccount(dataPath,
accountPath, final var pair = openFileChannel(fileName, true);
number, final var signalAccount = new SignalAccount(pair.first(), pair.second());
serviceEnvironment,
aci, signalAccount.dataPath = dataPath;
pni, signalAccount.accountPath = accountPath;
password, signalAccount.serviceEnvironment = serviceEnvironment;
encryptedDeviceName, signalAccount.aciAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
deviceId, signalAccount.pniAccountData.setLocalRegistrationId(KeyHelper.generateRegistrationId(false));
aciIdentityKey, signalAccount.settings = settings;
pniIdentityKey,
registrationId, signalAccount.previousStorageVersion = CURRENT_STORAGE_VERSION;
pniRegistrationId,
profileKey,
settings);
}
final var signalAccount = load(dataPath, accountPath, true, settings);
signalAccount.setProvisioningData(number,
aci,
pni,
password,
encryptedDeviceName,
deviceId,
aciIdentityKey,
pniIdentityKey,
profileKey);
signalAccount.getRecipientTrustedResolver()
.resolveSelfRecipientTrusted(signalAccount.getSelfRecipientAddress());
signalAccount.aciAccountData.getSessionStore().archiveAllSessions();
signalAccount.pniAccountData.getSessionStore().archiveAllSessions();
signalAccount.getSenderKeyStore().deleteAll();
signalAccount.clearAllPreKeys();
return signalAccount; return signalAccount;
} }
private void setProvisioningData( public void setProvisioningData(
final String number, final String number,
final ACI aci, final ACI aci,
final PNI pni, final PNI pni,
final String password, final String password,
final String encryptedDeviceName, final String encryptedDeviceName,
final int deviceId,
final IdentityKeyPair aciIdentity, final IdentityKeyPair aciIdentity,
final IdentityKeyPair pniIdentity, final IdentityKeyPair pniIdentity,
final ProfileKey profileKey final ProfileKey profileKey
) { ) {
this.deviceId = 0;
this.number = number; this.number = number;
this.aciAccountData.setServiceId(aci); this.aciAccountData.setServiceId(aci);
this.pniAccountData.setServiceId(pni); this.pniAccountData.setServiceId(pni);
getRecipientTrustedResolver().resolveSelfRecipientTrusted(getSelfRecipientAddress());
this.password = password; this.password = password;
this.profileKey = profileKey; this.profileKey = profileKey;
getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey()); getProfileStore().storeSelfProfileKey(getSelfRecipientId(), getProfileKey());
this.encryptedDeviceName = encryptedDeviceName; this.encryptedDeviceName = encryptedDeviceName;
this.deviceId = deviceId;
this.aciAccountData.setIdentityKeyPair(aciIdentity); this.aciAccountData.setIdentityKeyPair(aciIdentity);
this.pniAccountData.setIdentityKeyPair(pniIdentity); this.pniAccountData.setIdentityKeyPair(pniIdentity);
this.registered = true; this.registered = false;
this.isMultiDevice = true; this.isMultiDevice = true;
getKeyValueStore().storeEntry(lastReceiveTimestamp, 0L); getKeyValueStore().storeEntry(lastReceiveTimestamp, 0L);
this.pinMasterKey = null; this.pinMasterKey = null;
getKeyValueStore().storeEntry(storageManifestVersion, -1L); getKeyValueStore().storeEntry(storageManifestVersion, -1L);
this.setStorageManifest(null); this.setStorageManifest(null);
this.storageKey = null; this.storageKey = null;
getSenderKeyStore().deleteAll();
trustSelfIdentity(ServiceIdType.ACI); trustSelfIdentity(ServiceIdType.ACI);
trustSelfIdentity(ServiceIdType.PNI); trustSelfIdentity(ServiceIdType.PNI);
aciAccountData.getSessionStore().archiveAllSessions();
pniAccountData.getSessionStore().archiveAllSessions();
clearAllPreKeys();
getKeyValueStore().storeEntry(lastRecipientsRefresh, null); getKeyValueStore().storeEntry(lastRecipientsRefresh, null);
save();
}
public void finishLinking(final int deviceId) {
this.registered = true;
this.deviceId = deviceId;
save();
} }
public void finishRegistration( public void finishRegistration(
@ -476,7 +405,7 @@ public class SignalAccount implements Closeable {
return false; return false;
} }
var f = getFileName(dataPath, account); var f = getFileName(dataPath, account);
return !(!f.exists() || f.isDirectory()); return f.exists() && !f.isDirectory() && f.length() > 0L;
} }
private void load( private void load(