]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/helper/AccountHelper.java
Update libsignal-service
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / AccountHelper.java
index 5abac1e7b48754bbf38d6bcbe946266fd5cdcaa5..0710b50c030a4313f71001d07768ca500a15665b 100644 (file)
@@ -3,11 +3,10 @@ package org.asamk.signal.manager.helper;
 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.asamk.signal.manager.api.VerificationMethoNotAvailableException;
+import org.asamk.signal.manager.api.VerificationMethodNotAvailableException;
 import org.asamk.signal.manager.internal.SignalDependencies;
 import org.asamk.signal.manager.jobs.SyncStorageJob;
 import org.asamk.signal.manager.storage.SignalAccount;
@@ -27,6 +26,7 @@ import org.slf4j.Logger;
 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;
@@ -39,6 +39,7 @@ import org.whispersystems.signalservice.api.push.exceptions.UsernameIsNotReserve
 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;
@@ -55,6 +56,7 @@ 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 {
@@ -100,7 +102,7 @@ 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()
@@ -164,19 +166,24 @@ public class AccountHelper {
     }
 
     public void startChangeNumber(
-            String newNumber, boolean voiceVerification, String captcha
-    ) throws IOException, CaptchaRequiredException, NonNormalizedPhoneNumberException, RateLimitException, VerificationMethoNotAvailableException {
+            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 {
@@ -194,7 +201,9 @@ public class AccountHelper {
     }
 
     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>();
@@ -219,20 +228,30 @@ public class AccountHelper {
         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;
@@ -269,14 +288,13 @@ public class AccountHelper {
                 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,
@@ -296,9 +314,7 @@ public class AccountHelper {
         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) {
@@ -376,6 +392,7 @@ public class AccountHelper {
         account.setUsername(username.getUsername());
         account.setUsernameLink(linkComponents);
         account.getRecipientStore().resolveSelfRecipientTrusted(account.getSelfRecipientAddress());
+        account.getRecipientStore().rotateSelfStorageId();
         logger.debug("[confirmUsername] Successfully confirmed username.");
     }
 
@@ -409,6 +426,7 @@ public class AccountHelper {
                         e.getClass().getSimpleName());
                 account.setUsername(null);
                 account.setUsernameLink(null);
+                account.getRecipientStore().rotateSelfStorageId();
                 throw e;
             }
         } else {
@@ -431,6 +449,7 @@ public class AccountHelper {
             account.setUsernameLink(linkComponents);
             logger.debug("[confirmUsername] Successfully reclaimed existing username and link.");
         }
+        account.getRecipientStore().rotateSelfStorageId();
     }
 
     private void tryToSetUsernameLink(Username username) {
@@ -446,6 +465,8 @@ public class AccountHelper {
     }
 
     public void deleteUsername() throws IOException {
+        dependencies.getAccountManager().deleteUsernameLink();
+        account.setUsernameLink(null);
         dependencies.getAccountManager().deleteUsername();
         account.setUsername(null);
         logger.debug("[deleteUsername] Successfully deleted the username.");
@@ -461,21 +482,29 @@ public class AccountHelper {
         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());
     }
@@ -500,6 +529,7 @@ public class AccountHelper {
         dependencies.getAccountManager().enableRegistrationLock(masterKey);
 
         account.setRegistrationLockPin(pin);
+        updateAccountAttributes();
     }
 
     public void removeRegistrationPin() throws IOException {