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.exceptions.UsernameMalformedException;
import org.whispersystems.signalservice.api.push.exceptions.UsernameTakenException;
import org.whispersystems.signalservice.api.util.DeviceNameUtil;
+import org.whispersystems.signalservice.internal.push.DeviceLimitExceededException;
import org.whispersystems.signalservice.internal.push.KyberPreKeyEntity;
import org.whispersystems.signalservice.internal.push.OutgoingPushMessage;
import org.whispersystems.signalservice.internal.push.SyncMessage;
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
&& account.isPrimaryDevice()
}
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());
- String sessionId = NumberVerificationUtils.handleVerificationSession(accountManager,
+ final var registrationApi = accountManager.getRegistrationApi();
+ String sessionId = NumberVerificationUtils.handleVerificationSession(registrationApi,
account.getSessionId(newNumber),
id -> account.setSessionId(newNumber, id),
voiceVerification,
captcha);
- NumberVerificationUtils.requestVerificationCode(accountManager, sessionId, voiceVerification);
+ NumberVerificationUtils.requestVerificationCode(registrationApi, sessionId, voiceVerification);
}
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>();
final var messageSender = dependencies.getMessageSender();
for (final var deviceId : deviceIds) {
// Signed Prekey
- final var signedPreKeyRecord = KeyUtils.generateSignedPreKeyRecord(KeyUtils.getRandomInt(PREKEY_MAXIMUM_ID),
- pniIdentity.getPrivateKey());
- final var signedPreKeyEntity = new SignedPreKeyEntity(signedPreKeyRecord.getId(),
- signedPreKeyRecord.getKeyPair().getPublicKey(),
- signedPreKeyRecord.getSignature());
- devicePniSignedPreKeys.put(deviceId, signedPreKeyEntity);
+ final SignedPreKeyRecord signedPreKeyRecord;
+ try {
+ signedPreKeyRecord = KeyUtils.generateSignedPreKeyRecord(KeyUtils.getRandomInt(PREKEY_MAXIMUM_ID),
+ pniIdentity.getPrivateKey());
+ final var signedPreKeyEntity = new SignedPreKeyEntity(signedPreKeyRecord.getId(),
+ signedPreKeyRecord.getKeyPair().getPublicKey(),
+ signedPreKeyRecord.getSignature());
+ devicePniSignedPreKeys.put(deviceId, signedPreKeyEntity);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError("unexpected invalid key", e);
+ }
// Last-resort kyber prekey
- final var lastResortKyberPreKeyRecord = KeyUtils.generateKyberPreKeyRecord(KeyUtils.getRandomInt(
- PREKEY_MAXIMUM_ID), pniIdentity.getPrivateKey());
- final var kyberPreKeyEntity = new KyberPreKeyEntity(lastResortKyberPreKeyRecord.getId(),
- lastResortKyberPreKeyRecord.getKeyPair().getPublicKey(),
- lastResortKyberPreKeyRecord.getSignature());
- devicePniLastResortKyberPreKeys.put(deviceId, kyberPreKeyEntity);
+ final KyberPreKeyRecord lastResortKyberPreKeyRecord;
+ try {
+ lastResortKyberPreKeyRecord = KeyUtils.generateKyberPreKeyRecord(KeyUtils.getRandomInt(PREKEY_MAXIMUM_ID),
+ pniIdentity.getPrivateKey());
+ final var kyberPreKeyEntity = new KyberPreKeyEntity(lastResortKyberPreKeyRecord.getId(),
+ lastResortKyberPreKeyRecord.getKeyPair().getPublicKey(),
+ lastResortKyberPreKeyRecord.getSignature());
+ devicePniLastResortKyberPreKeys.put(deviceId, kyberPreKeyEntity);
+ } catch (InvalidKeyException e) {
+ throw new AssertionError("unexpected invalid key", e);
+ }
// Registration Id
var pniRegistrationId = -1;
pin,
context.getPinHelper(),
(sessionId1, verificationCode1, registrationLock) -> {
- final var accountManager = dependencies.getAccountManager();
+ final var registrationApi = dependencies.getRegistrationApi();
try {
- Utils.handleResponseException(accountManager.verifyAccount(verificationCode1, sessionId1));
+ handleResponseException(registrationApi.verifyAccount(sessionId1, verificationCode1));
} catch (AlreadyVerifiedException e) {
// Already verified so can continue changing number
}
- return Utils.handleResponseException(accountManager.changeNumber(new ChangePhoneNumberRequest(
- sessionId1,
+ return handleResponseException(registrationApi.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) {
account.setUsername(username.getUsername());
account.setUsernameLink(linkComponents);
account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
+ account.getRecipientStore().rotateSelfStorageId();
logger.debug("[confirmUsername] Successfully confirmed username.");
}
e.getClass().getSimpleName());
account.setUsername(null);
account.setUsernameLink(null);
+ account.getRecipientStore().rotateSelfStorageId();
throw e;
}
} else {
account.setUsernameLink(linkComponents);
logger.debug("[confirmUsername] Successfully reclaimed existing username and link.");
}
+ account.getRecipientStore().rotateSelfStorageId();
}
private void tryToSetUsernameLink(Username username) {
dependencies.getAccountManager().setAccountAttributes(account.getAccountAttributes(null));
}
- public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, InvalidDeviceLinkException {
- var verificationCode = dependencies.getAccountManager().getNewDeviceVerificationCode();
-
+ public void addDevice(DeviceLinkUrl deviceLinkInfo) throws IOException, org.asamk.signal.manager.api.DeviceLimitExceededException {
+ final var linkDeviceApi = dependencies.getLinkDeviceApi();
+ final LinkedDeviceVerificationCodeResponse verificationCode;
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);
+ verificationCode = handleResponseException(linkDeviceApi.getDeviceVerificationCode());
+ } catch (DeviceLimitExceededException e) {
+ throw new org.asamk.signal.manager.api.DeviceLimitExceededException("Too many linked devices", e);
}
+
+ handleResponseException(dependencies.getLinkDeviceApi()
+ .linkDevice(account.getNumber(),
+ account.getAci(),
+ account.getPni(),
+ deviceLinkInfo.deviceIdentifier(),
+ deviceLinkInfo.deviceKey(),
+ account.getAciIdentityKeyPair(),
+ account.getPniIdentityKeyPair(),
+ account.getProfileKey(),
+ account.getOrCreatePinMasterKey(),
+ account.getOrCreateMediaRootBackupKey(),
+ account.getOrCreateAccountEntropyPool(),
+ verificationCode.getVerificationCode(),
+ null));
account.setMultiDevice(true);
context.getJobExecutor().enqueueJob(new SyncStorageJob());
}