import org.asamk.signal.manager.api.CaptchaRequiredException;
import org.asamk.signal.manager.api.DeviceLinkUrl;
import org.asamk.signal.manager.api.IncorrectPinException;
-import org.asamk.signal.manager.api.InvalidDeviceLinkException;
import org.asamk.signal.manager.api.NonNormalizedPhoneNumberException;
import org.asamk.signal.manager.api.PinLockedException;
import org.asamk.signal.manager.api.RateLimitException;
import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.account.ChangePhoneNumberRequest;
import org.whispersystems.signalservice.api.crypto.UntrustedIdentityException;
+import org.whispersystems.signalservice.api.link.LinkedDeviceVerificationCodeResponse;
import org.whispersystems.signalservice.api.push.ServiceId.ACI;
import org.whispersystems.signalservice.api.push.ServiceId.PNI;
import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity;
+import org.whispersystems.signalservice.api.push.UsernameLinkComponents;
import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException;
import org.whispersystems.signalservice.api.push.exceptions.AuthorizationFailedException;
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
-import java.util.Optional;
+import java.util.UUID;
import java.util.concurrent.TimeUnit;
import okio.ByteString;
import static org.asamk.signal.manager.config.ServiceConfig.PREKEY_MAXIMUM_ID;
+import static org.asamk.signal.manager.util.Utils.handleResponseException;
import static org.whispersystems.signalservice.internal.util.Util.isEmpty;
public class AccountHelper {
checkWhoAmiI();
}
if (!account.isPrimaryDevice() && account.getPniIdentityKeyPair() == null) {
- context.getSyncHelper().requestSyncPniIdentity();
+ throw new IOException("Missing PNI identity key, relinking required");
}
- if (account.getPreviousStorageVersion() < 4
+ if (account.getPreviousStorageVersion() < 10
&& account.isPrimaryDevice()
&& account.getRegistrationLockPin() != null) {
migrateRegistrationPin();
}
public void startChangeNumber(
- String newNumber, boolean voiceVerification, String captcha
+ String newNumber,
+ boolean voiceVerification,
+ String captcha
) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, RateLimitException, VerificationMethodNotAvailableException {
final var accountManager = dependencies.createUnauthenticatedAccountManager(newNumber, account.getPassword());
final var registrationApi = accountManager.getRegistrationApi();
}
public void finishChangeNumber(
- String newNumber, String verificationCode, String pin
+ String newNumber,
+ String verificationCode,
+ String pin
) throws IncorrectPinException, PinLockedException, IOException {
for (var attempts = 0; attempts < 5; attempts++) {
try {
}
private void finishChangeNumberInternal(
- String newNumber, String verificationCode, String pin
+ String newNumber,
+ String verificationCode,
+ String pin
) throws IncorrectPinException, PinLockedException, IOException {
final var pniIdentity = KeyUtils.generateIdentityKeyPair();
final var encryptedDeviceMessages = new ArrayList<OutgoingPushMessage>();
context.getPinHelper(),
(sessionId1, verificationCode1, registrationLock) -> {
final var registrationApi = dependencies.getRegistrationApi();
+ final var accountApi = dependencies.getAccountApi();
try {
- Utils.handleResponseException(registrationApi.verifyAccount(verificationCode1, sessionId1));
+ handleResponseException(registrationApi.verifyAccount(sessionId1, verificationCode1));
} catch (AlreadyVerifiedException e) {
// Already verified so can continue changing number
}
- return Utils.handleResponseException(registrationApi.changeNumber(new ChangePhoneNumberRequest(
- sessionId1,
+ return handleResponseException(accountApi.changeNumber(new ChangePhoneNumberRequest(sessionId1,
null,
newNumber,
registrationLock,
handlePniChangeNumberMessage(selfChangeNumber, updatePni);
}
- public void handlePniChangeNumberMessage(
- final SyncMessage.PniChangeNumber pniChangeNumber, final PNI updatedPni
- ) {
+ public void handlePniChangeNumberMessage(final SyncMessage.PniChangeNumber pniChangeNumber, final PNI updatedPni) {
if (pniChangeNumber.identityKeyPair != null
&& pniChangeNumber.registrationId != null
&& pniChangeNumber.signedPreKey != null) {
candidateHashes.add(Base64.encodeUrlSafeWithoutPadding(candidate.getHash()));
}
- final var response = dependencies.getAccountManager().reserveUsername(candidateHashes);
+ final var response = handleResponseException(dependencies.getAccountApi().reserveUsername(candidateHashes));
final var hashIndex = candidateHashes.indexOf(response.getUsernameHash());
if (hashIndex == -1) {
logger.warn("[reserveUsername] The response hash could not be found in our set of candidateHashes.");
logger.debug("[reserveUsername] Successfully reserved username.");
final var username = candidates.get(hashIndex);
- final var linkComponents = dependencies.getAccountManager().confirmUsernameAndCreateNewLink(username);
+ final var linkComponents = confirmUsernameAndCreateNewLink(username);
account.setUsername(username.getUsername());
account.setUsernameLink(linkComponents);
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
logger.debug("[confirmUsername] Successfully confirmed username.");
}
+ public UsernameLinkComponents createUsernameLink(Username username) throws IOException {
+ try {
+ Username.UsernameLink link = username.generateLink();
+ return handleResponseException(dependencies.getAccountApi().createUsernameLink(link));
+ } catch (BaseUsernameException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private UsernameLinkComponents confirmUsernameAndCreateNewLink(Username username) throws IOException {
+ try {
+ Username.UsernameLink link = username.generateLink();
+ UUID serverId = handleResponseException(dependencies.getAccountApi().confirmUsername(username, link));
+
+ return new UsernameLinkComponents(link.getEntropy(), serverId);
+ } catch (BaseUsernameException e) {
+ throw new AssertionError(e);
+ }
+ }
+
+ private UsernameLinkComponents reclaimUsernameAndLink(
+ Username username,
+ UsernameLinkComponents linkComponents
+ ) throws IOException {
+ try {
+ Username.UsernameLink link = username.generateLink(linkComponents.getEntropy());
+ UUID serverId = handleResponseException(dependencies.getAccountApi().confirmUsername(username, link));
+
+ return new UsernameLinkComponents(link.getEntropy(), serverId);
+ } catch (BaseUsernameException e) {
+ throw new AssertionError(e);
+ }
+ }
+
public void refreshCurrentUsername() throws IOException, BaseUsernameException {
final var localUsername = account.getUsername();
if (localUsername == null) {
final var usernameLink = account.getUsernameLink();
if (usernameLink == null) {
- dependencies.getAccountManager()
- .reserveUsername(List.of(Base64.encodeUrlSafeWithoutPadding(username.getHash())));
+ handleResponseException(dependencies.getAccountApi()
+ .reserveUsername(List.of(Base64.encodeUrlSafeWithoutPadding(username.getHash()))));
logger.debug("[reserveUsername] Successfully reserved existing username.");
- final var linkComponents = dependencies.getAccountManager().confirmUsernameAndCreateNewLink(username);
+ final var linkComponents = confirmUsernameAndCreateNewLink(username);
account.setUsernameLink(linkComponents);
logger.debug("[confirmUsername] Successfully confirmed existing username.");
} else {
- final var linkComponents = dependencies.getAccountManager().reclaimUsernameAndLink(username, usernameLink);
+ final var linkComponents = reclaimUsernameAndLink(username, usernameLink);
account.setUsernameLink(linkComponents);
logger.debug("[confirmUsername] Successfully reclaimed existing username and link.");
}
private void tryToSetUsernameLink(Username username) {
for (var i = 1; i < 4; i++) {
try {
- final var linkComponents = dependencies.getAccountManager().createUsernameLink(username);
+ final var linkComponents = createUsernameLink(username);
account.setUsernameLink(linkComponents);
break;
} catch (IOException e) {
}
public void deleteUsername() throws IOException {
- dependencies.getAccountManager().deleteUsernameLink();
+ handleResponseException(dependencies.getAccountApi().deleteUsername());
account.setUsernameLink(null);
- dependencies.getAccountManager().deleteUsername();
account.setUsername(null);
logger.debug("[deleteUsername] Successfully deleted the username.");
}
}
public void updateAccountAttributes() throws IOException {
- dependencies.getAccountManager().setAccountAttributes(account.getAccountAttributes(null));
+ handleResponseException(dependencies.getAccountApi().setAccountAttributes(account.getAccountAttributes(null)));
}
- public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, InvalidDeviceLinkException, org.asamk.signal.manager.api.DeviceLimitExceededException {
- String verificationCode;
+ public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, org.asamk.signal.manager.api.DeviceLimitExceededException {
+ final var linkDeviceApi = dependencies.getLinkDeviceApi();
+ final LinkedDeviceVerificationCodeResponse verificationCode;
try {
- verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
+ verificationCode = handleResponseException(linkDeviceApi.getDeviceVerificationCode());
} catch (DeviceLimitExceededException e) {
throw new org.asamk.signal.manager.api.DeviceLimitExceededException("Too many linked devices", e);
}
- try {
- dependencies.getAccountManager()
- .addDevice(deviceLinkInfo.deviceIdentifier(),
- deviceLinkInfo.deviceKey(),
- account.getAciIdentityKeyPair(),
- account.getPniIdentityKeyPair(),
- account.getProfileKey(),
- account.getOrCreatePinMasterKey(),
- verificationCode);
- } catch (InvalidKeyException e) {
- throw new InvalidDeviceLinkException("Invalid device link", e);
- }
+ handleResponseException(dependencies.getLinkDeviceApi()
+ .linkDevice(account.getNumber(),
+ account.getAci(),
+ account.getPni(),
+ deviceLinkInfo.deviceIdentifier(),
+ deviceLinkInfo.deviceKey(),
+ account.getAciIdentityKeyPair(),
+ account.getPniIdentityKeyPair(),
+ account.getProfileKey(),
+ account.getOrCreateAccountEntropyPool(),
+ account.getOrCreatePinMasterKey(),
+ account.getOrCreateMediaRootBackupKey(),
+ verificationCode.getVerificationCode(),
+ null));
account.setMultiDevice(true);
context.getJobExecutor().enqueueJob(new SyncStorageJob());
}
public void removeLinkedDevices(int deviceId) throws IOException {
- dependencies.getAccountManager().removeDevice(deviceId);
- var devices = dependencies.getAccountManager().getDevices();
+ handleResponseException(dependencies.getLinkDeviceApi().removeDevice(deviceId));
+ var devices = handleResponseException(dependencies.getLinkDeviceApi().getDevices());
account.setMultiDevice(devices.size() > 1);
}
var masterKey = account.getOrCreatePinMasterKey();
context.getPinHelper().migrateRegistrationLockPin(account.getRegistrationLockPin(), masterKey);
- dependencies.getAccountManager().enableRegistrationLock(masterKey);
+ handleResponseException(dependencies.getAccountApi()
+ .enableRegistrationLock(masterKey.deriveRegistrationLock()));
}
public void setRegistrationPin(String pin) throws IOException {
var masterKey = account.getOrCreatePinMasterKey();
context.getPinHelper().setRegistrationLockPin(pin, masterKey);
- dependencies.getAccountManager().enableRegistrationLock(masterKey);
+ handleResponseException(dependencies.getAccountApi()
+ .enableRegistrationLock(masterKey.deriveRegistrationLock()));
account.setRegistrationLockPin(pin);
updateAccountAttributes();
public void removeRegistrationPin() throws IOException {
// Remove KBS Pin
context.getPinHelper().removeRegistrationLockPin();
- dependencies.getAccountManager().disableRegistrationLock();
+ handleResponseException(dependencies.getAccountApi().disableRegistrationLock());
account.setRegistrationLockPin(null);
}
// When setting an empty GCM id, the Signal-Server also sets the fetchesMessages property to false.
// If this is the primary device, other users can't send messages to this number anymore.
// If this is a linked device, other users can still send messages, but this device doesn't receive them anymore.
- dependencies.getAccountManager().setGcmId(Optional.empty());
+ handleResponseException(dependencies.getAccountApi().clearFcmToken());
account.setRegistered(false);
unregisteredListener.call();
}
account.setRegistrationLockPin(null);
- dependencies.getAccountManager().deleteAccount();
+ handleResponseException(dependencies.getAccountApi().deleteAccount());
account.setRegistered(false);
unregisteredListener.call();