*/
package org.asamk.signal.manager;
+import org.asamk.signal.manager.config.ServiceConfig;
+import org.asamk.signal.manager.config.ServiceEnvironment;
+import org.asamk.signal.manager.config.ServiceEnvironmentConfig;
import org.asamk.signal.manager.helper.PinHelper;
import org.asamk.signal.manager.storage.SignalAccount;
+import org.asamk.signal.manager.storage.identities.TrustNewIdentity;
import org.asamk.signal.manager.util.KeyUtils;
-import org.signal.zkgroup.profiles.ProfileKey;
-import org.whispersystems.libsignal.IdentityKeyPair;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.whispersystems.libsignal.util.KeyHelper;
import org.whispersystems.libsignal.util.guava.Optional;
-import org.whispersystems.signalservice.api.KbsPinData;
-import org.whispersystems.signalservice.api.KeyBackupService;
import org.whispersystems.signalservice.api.KeyBackupServicePinException;
import org.whispersystems.signalservice.api.KeyBackupSystemNoDataException;
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
+import org.whispersystems.signalservice.api.groupsv2.ClientZkOperations;
+import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
+import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
-import org.whispersystems.signalservice.api.util.SleepTimer;
-import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.util.UuidUtil;
-import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
+import org.whispersystems.signalservice.internal.ServiceResponse;
import org.whispersystems.signalservice.internal.push.LockedException;
+import org.whispersystems.signalservice.internal.push.RequestVerificationCodeResponse;
import org.whispersystems.signalservice.internal.push.VerifyAccountResponse;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
public class RegistrationManager implements Closeable {
+ private final static Logger logger = LoggerFactory.getLogger(RegistrationManager.class);
+
private SignalAccount account;
private final PathConfig pathConfig;
- private final SignalServiceConfiguration serviceConfiguration;
+ private final ServiceEnvironmentConfig serviceEnvironmentConfig;
private final String userAgent;
private final SignalServiceAccountManager accountManager;
public RegistrationManager(
SignalAccount account,
PathConfig pathConfig,
- SignalServiceConfiguration serviceConfiguration,
+ ServiceEnvironmentConfig serviceEnvironmentConfig,
String userAgent
) {
this.account = account;
this.pathConfig = pathConfig;
- this.serviceConfiguration = serviceConfiguration;
+ this.serviceEnvironmentConfig = serviceEnvironmentConfig;
this.userAgent = userAgent;
- final SleepTimer timer = new UptimeSleepTimer();
- this.accountManager = new SignalServiceAccountManager(serviceConfiguration, new DynamicCredentialsProvider(
- // Using empty UUID, because registering doesn't work otherwise
- null,
- account.getUsername(),
- account.getPassword(),
- account.getSignalingKey(),
- SignalServiceAddress.DEFAULT_DEVICE_ID), userAgent, null, ServiceConfig.AUTOMATIC_NETWORK_RETRY, timer);
- final KeyBackupService keyBackupService = ServiceConfig.createKeyBackupService(accountManager);
+ GroupsV2Operations groupsV2Operations;
+ try {
+ groupsV2Operations = new GroupsV2Operations(ClientZkOperations.create(serviceEnvironmentConfig.getSignalServiceConfiguration()));
+ } catch (Throwable ignored) {
+ groupsV2Operations = null;
+ }
+ this.accountManager = new SignalServiceAccountManager(serviceEnvironmentConfig.getSignalServiceConfiguration(),
+ new DynamicCredentialsProvider(
+ // Using empty UUID, because registering doesn't work otherwise
+ null, account.getUsername(), account.getPassword(), SignalServiceAddress.DEFAULT_DEVICE_ID),
+ userAgent,
+ groupsV2Operations,
+ ServiceConfig.AUTOMATIC_NETWORK_RETRY);
+ final var keyBackupService = accountManager.getKeyBackupService(ServiceConfig.getIasKeyStore(),
+ serviceEnvironmentConfig.getKeyBackupConfig().getEnclaveName(),
+ serviceEnvironmentConfig.getKeyBackupConfig().getServiceId(),
+ serviceEnvironmentConfig.getKeyBackupConfig().getMrenclave(),
+ 10);
this.pinHelper = new PinHelper(keyBackupService);
}
public static RegistrationManager init(
- String username, File settingsPath, SignalServiceConfiguration serviceConfiguration, String userAgent
+ String username, File settingsPath, ServiceEnvironment serviceEnvironment, String userAgent
) throws IOException {
- PathConfig pathConfig = PathConfig.createDefault(settingsPath);
+ var pathConfig = PathConfig.createDefault(settingsPath);
+ final var serviceConfiguration = ServiceConfig.getServiceEnvironmentConfig(serviceEnvironment, userAgent);
if (!SignalAccount.userExists(pathConfig.getDataPath(), username)) {
- IdentityKeyPair identityKey = KeyUtils.generateIdentityKeyPair();
- int registrationId = KeyHelper.generateRegistrationId(false);
+ var identityKey = KeyUtils.generateIdentityKeyPair();
+ var registrationId = KeyHelper.generateRegistrationId(false);
- ProfileKey profileKey = KeyUtils.createProfileKey();
- SignalAccount account = SignalAccount.create(pathConfig.getDataPath(),
+ var profileKey = KeyUtils.createProfileKey();
+ var account = SignalAccount.create(pathConfig.getDataPath(),
username,
identityKey,
registrationId,
- profileKey);
- account.save();
+ profileKey,
+ TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
}
- SignalAccount account = SignalAccount.load(pathConfig.getDataPath(), username);
+ var account = SignalAccount.load(pathConfig.getDataPath(), username, true, TrustNewIdentity.ON_FIRST_USE);
return new RegistrationManager(account, pathConfig, serviceConfiguration, userAgent);
}
public void register(boolean voiceVerification, String captcha) throws IOException {
- if (account.getPassword() == null) {
- account.setPassword(KeyUtils.createPassword());
- }
-
+ final ServiceResponse<RequestVerificationCodeResponse> response;
if (voiceVerification) {
- accountManager.requestVoiceVerificationCode(Locale.getDefault(),
+ response = accountManager.requestVoiceVerificationCode(getDefaultLocale(),
Optional.fromNullable(captcha),
+ Optional.absent(),
Optional.absent());
} else {
- accountManager.requestSmsVerificationCode(false, Optional.fromNullable(captcha), Optional.absent());
+ response = accountManager.requestSmsVerificationCode(false,
+ Optional.fromNullable(captcha),
+ Optional.absent(),
+ Optional.absent());
+ }
+ handleResponseException(response);
+ }
+
+ private Locale getDefaultLocale() {
+ final var locale = Locale.getDefault();
+ try {
+ Locale.LanguageRange.parse(locale.getLanguage() + "-" + locale.getCountry());
+ } catch (IllegalArgumentException e) {
+ logger.debug("Invalid locale, ignoring: {}", locale);
+ return null;
}
- account.save();
+ return locale;
}
- public void verifyAccount(
+ public Manager verifyAccount(
String verificationCode, String pin
- ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
+ ) throws IOException, LockedException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
verificationCode = verificationCode.replace("-", "");
- if (account.getSignalingKey() == null) {
- account.setSignalingKey(KeyUtils.createSignalingKey());
- }
VerifyAccountResponse response;
+ MasterKey masterKey;
try {
- response = verifyAccountWithCode(verificationCode, pin, null);
- account.setPinMasterKey(null);
+ response = verifyAccountWithCode(verificationCode, null);
+
+ masterKey = null;
+ pin = null;
} catch (LockedException e) {
if (pin == null) {
throw e;
}
- KbsPinData registrationLockData = pinHelper.getRegistrationLockData(pin, e);
+ var registrationLockData = pinHelper.getRegistrationLockData(pin, e);
if (registrationLockData == null) {
throw e;
}
- String registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
+ var registrationLock = registrationLockData.getMasterKey().deriveRegistrationLock();
try {
- response = verifyAccountWithCode(verificationCode, null, registrationLock);
+ response = verifyAccountWithCode(verificationCode, registrationLock);
} catch (LockedException _e) {
throw new AssertionError("KBS Pin appeared to matched but reg lock still failed!");
}
- account.setPinMasterKey(registrationLockData.getMasterKey());
+ masterKey = registrationLockData.getMasterKey();
}
// TODO response.isStorageCapable()
//accountManager.setGcmId(Optional.of(GoogleCloudMessaging.getInstance(this).register(REGISTRATION_ID)));
+ account.finishRegistration(UuidUtil.parseOrNull(response.getUuid()), masterKey, pin);
- account.setDeviceId(SignalServiceAddress.DEFAULT_DEVICE_ID);
- account.setMultiDevice(false);
- account.setRegistered(true);
- account.setUuid(UuidUtil.parseOrNull(response.getUuid()));
- account.setRegistrationLockPin(pin);
- account.getSignalProtocolStore()
- .saveIdentity(account.getSelfAddress(),
- account.getSignalProtocolStore().getIdentityKeyPair().getPublicKey(),
- TrustLevel.TRUSTED_VERIFIED);
-
- try (Manager m = new Manager(account, pathConfig, serviceConfiguration, userAgent)) {
+ Manager m = null;
+ try {
+ m = new Manager(account, pathConfig, serviceEnvironmentConfig, userAgent);
+ account = null;
m.refreshPreKeys();
+ // Set an initial empty profile so user can be added to groups
+ m.setProfile(null, null, null, null, null);
- m.close(false);
- }
+ final var result = m;
+ m = null;
- account.save();
+ return result;
+ } finally {
+ if (m != null) {
+ m.close();
+ }
+ }
}
private VerifyAccountResponse verifyAccountWithCode(
- final String verificationCode, final String legacyPin, final String registrationLock
+ final String verificationCode, final String registrationLock
) throws IOException {
- return accountManager.verifyAccountWithCode(verificationCode,
- account.getSignalingKey(),
- account.getSignalProtocolStore().getLocalRegistrationId(),
- true,
- legacyPin,
- registrationLock,
- account.getSelfUnidentifiedAccessKey(),
- account.isUnrestrictedUnidentifiedAccess(),
- ServiceConfig.capabilities,
- account.isDiscoverableByPhoneNumber());
+ final ServiceResponse<VerifyAccountResponse> response;
+ if (registrationLock == null) {
+ response = accountManager.verifyAccount(verificationCode,
+ account.getLocalRegistrationId(),
+ true,
+ account.getSelfUnidentifiedAccessKey(),
+ account.isUnrestrictedUnidentifiedAccess(),
+ ServiceConfig.capabilities,
+ account.isDiscoverableByPhoneNumber());
+ } else {
+ response = accountManager.verifyAccountWithRegistrationLockPin(verificationCode,
+ account.getLocalRegistrationId(),
+ true,
+ registrationLock,
+ account.getSelfUnidentifiedAccessKey(),
+ account.isUnrestrictedUnidentifiedAccess(),
+ ServiceConfig.capabilities,
+ account.isDiscoverableByPhoneNumber());
+ }
+ handleResponseException(response);
+ return response.getResult().get();
}
@Override
account = null;
}
}
+
+ private void handleResponseException(final ServiceResponse<?> response) throws IOException {
+ final var throwableOptional = response.getExecutionError().or(response.getApplicationError());
+ if (throwableOptional.isPresent()) {
+ if (throwableOptional.get() instanceof IOException) {
+ throw (IOException) throwableOptional.get();
+ } else {
+ throw new IOException(throwableOptional.get());
+ }
+ }
+ }
}