]> nmode's Git Repositories - signal-cli/blobdiff - lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java
Improve behavior when pin data doesn't exist on the server
[signal-cli] / lib / src / main / java / org / asamk / signal / manager / helper / PinHelper.java
index 6bc3978fc7485947e0051e6b981718f06b90e211..40878647079cdae284d3cac036e104cad7b40ad3 100644 (file)
 package org.asamk.signal.manager.helper;
 
 import org.asamk.signal.manager.api.IncorrectPinException;
-import org.asamk.signal.manager.api.Pair;
-import org.signal.libsignal.protocol.InvalidKeyException;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-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.kbs.MasterKey;
-import org.whispersystems.signalservice.api.kbs.PinHashUtil;
-import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
-import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
+import org.whispersystems.signalservice.api.svr.SecureValueRecovery;
+import org.whispersystems.signalservice.internal.push.AuthCredentials;
 import org.whispersystems.signalservice.internal.push.LockedException;
 
 import java.io.IOException;
-import java.util.Collection;
-import java.util.stream.Stream;
+import java.util.List;
 
 public class PinHelper {
 
-    private final static Logger logger = LoggerFactory.getLogger(PinHelper.class);
+    private static final Logger logger = LoggerFactory.getLogger(PinHelper.class);
 
-    private final KeyBackupService keyBackupService;
-    private final Collection<KeyBackupService> fallbackKeyBackupServices;
+    private final List<SecureValueRecovery> secureValueRecoveries;
 
-    public PinHelper(
-            final KeyBackupService keyBackupService, final Collection<KeyBackupService> fallbackKeyBackupServices
-    ) {
-        this.keyBackupService = keyBackupService;
-        this.fallbackKeyBackupServices = fallbackKeyBackupServices;
+    public PinHelper(final List<SecureValueRecovery> secureValueRecoveries) {
+        this.secureValueRecoveries = secureValueRecoveries;
     }
 
-    public void setRegistrationLockPin(
-            String pin, MasterKey masterKey
-    ) throws IOException {
-        final var pinChangeSession = keyBackupService.newPinChangeSession();
-        final var hashedPin = PinHashUtil.hashPin(pin, pinChangeSession.hashSalt());
-
-        try {
-            pinChangeSession.setPin(hashedPin, masterKey);
-        } catch (UnauthenticatedResponseException e) {
-            throw new IOException(e);
+    public void setRegistrationLockPin(String pin, MasterKey masterKey) throws IOException {
+        IOException exception = null;
+        for (final var secureValueRecovery : secureValueRecoveries) {
+            try {
+                final var backupResponse = secureValueRecovery.setPin(pin, masterKey).execute();
+                switch (backupResponse) {
+                    case SecureValueRecovery.BackupResponse.Success success -> {
+                    }
+                    case SecureValueRecovery.BackupResponse.ServerRejected serverRejected ->
+                            logger.warn("Backup svr failed: ServerRejected");
+                    case SecureValueRecovery.BackupResponse.EnclaveNotFound enclaveNotFound ->
+                            logger.warn("Backup svr failed: EnclaveNotFound");
+                    case SecureValueRecovery.BackupResponse.ExposeFailure exposeFailure ->
+                            logger.warn("Backup svr failed: ExposeFailure");
+                    case SecureValueRecovery.BackupResponse.ApplicationError error ->
+                            throw new IOException(error.getException());
+                    case SecureValueRecovery.BackupResponse.NetworkError error -> throw error.getException();
+                    case null, default -> throw new AssertionError("Unexpected response");
+                }
+            } catch (IOException e) {
+                exception = e;
+            }
+        }
+        if (exception != null) {
+            throw exception;
         }
-        pinChangeSession.enableRegistrationLock(masterKey);
     }
 
     public void migrateRegistrationLockPin(String pin, MasterKey masterKey) throws IOException {
         setRegistrationLockPin(pin, masterKey);
+    }
 
-        for (final var keyBackupService : fallbackKeyBackupServices) {
+    public void removeRegistrationLockPin() throws IOException {
+        IOException exception = null;
+        for (final var secureValueRecovery : secureValueRecoveries) {
             try {
-                final var pinChangeSession = keyBackupService.newPinChangeSession();
-                pinChangeSession.removePin();
-            } catch (Exception e) {
-                logger.warn("Failed to remove PIN from fallback KBS: {}", e.getMessage());
+                final var deleteResponse = secureValueRecovery.deleteData();
+                switch (deleteResponse) {
+                    case SecureValueRecovery.DeleteResponse.Success success -> {
+                    }
+                    case SecureValueRecovery.DeleteResponse.ServerRejected serverRejected ->
+                            logger.warn("Delete svr2 failed: ServerRejected");
+                    case SecureValueRecovery.DeleteResponse.EnclaveNotFound enclaveNotFound ->
+                            logger.warn("Delete svr2 failed: EnclaveNotFound");
+                    case SecureValueRecovery.DeleteResponse.ApplicationError error ->
+                            throw new IOException(error.getException());
+                    case SecureValueRecovery.DeleteResponse.NetworkError error -> throw error.getException();
+                    case null, default -> throw new AssertionError("Unexpected response");
+                }
+            } catch (IOException e) {
+                exception = e;
             }
         }
-    }
-
-    public void removeRegistrationLockPin() throws IOException {
-        final var pinChangeSession = keyBackupService.newPinChangeSession();
-        pinChangeSession.disableRegistrationLock();
-        try {
-            pinChangeSession.removePin();
-        } catch (UnauthenticatedResponseException e) {
-            throw new IOException(e);
+        if (exception != null) {
+            throw exception;
         }
     }
 
-    public KbsPinData getRegistrationLockData(
-            String pin, LockedException e
+    public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData(
+            String pin,
+            LockedException lockedException
     ) throws IOException, IncorrectPinException {
-        var basicStorageCredentials = e.getBasicStorageCredentials();
-        if (basicStorageCredentials == null) {
-            return null;
+        var svr2Credentials = lockedException.getSvr2Credentials();
+        if (svr2Credentials != null) {
+            IOException exception = null;
+            for (final var secureValueRecovery : secureValueRecoveries) {
+                try {
+                    final var lockData = getRegistrationLockData(secureValueRecovery, svr2Credentials, pin);
+                    if (lockData == null) {
+                        continue;
+                    }
+                    return lockData;
+                } catch (IOException e) {
+                    exception = e;
+                }
+            }
+            if (exception != null) {
+                throw exception;
+            }
         }
 
-        try {
-            return getRegistrationLockData(pin, basicStorageCredentials);
-        } catch (KeyBackupSystemNoDataException ex) {
-            throw new IOException(e);
-        } catch (KeyBackupServicePinException ex) {
-            throw new IncorrectPinException(ex.getTriesRemaining());
-        }
+        return null;
     }
 
-    private KbsPinData getRegistrationLockData(
-            String pin, String basicStorageCredentials
-    ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
-        var tokenResponsePair = getTokenResponse(basicStorageCredentials);
-        final var tokenResponse = tokenResponsePair.first();
-        final var keyBackupService = tokenResponsePair.second();
-
-        var registrationLockData = restoreMasterKey(pin, basicStorageCredentials, tokenResponse, keyBackupService);
-        if (registrationLockData == null) {
-            throw new AssertionError("Failed to restore master key");
-        }
-        return registrationLockData;
-    }
+    public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData(
+            SecureValueRecovery secureValueRecovery,
+            AuthCredentials authCredentials,
+            String pin
+    ) throws IOException, IncorrectPinException {
+        final var restoreResponse = secureValueRecovery.restoreDataPreRegistration(authCredentials, null, pin);
 
-    private Pair<TokenResponse, KeyBackupService> getTokenResponse(String basicStorageCredentials) throws IOException {
-        final var keyBackupServices = Stream.concat(Stream.of(keyBackupService), fallbackKeyBackupServices.stream())
-                .toList();
-        for (final var keyBackupService : keyBackupServices) {
-            var tokenResponse = keyBackupService.getToken(basicStorageCredentials);
-            if (tokenResponse != null && tokenResponse.getTries() > 0) {
-                return new Pair<>(tokenResponse, keyBackupService);
+        switch (restoreResponse) {
+            case SecureValueRecovery.RestoreResponse.Success s -> {
+                return s;
             }
-        }
-        throw new IOException("KBS Account locked, maximum pin attempts reached.");
-    }
-
-    private KbsPinData restoreMasterKey(
-            String pin,
-            String basicStorageCredentials,
-            TokenResponse tokenResponse,
-            final KeyBackupService keyBackupService
-    ) throws IOException, KeyBackupSystemNoDataException, KeyBackupServicePinException {
-        if (pin == null) return null;
-
-        if (basicStorageCredentials == null) {
-            throw new AssertionError("Cannot restore KBS key, no storage credentials supplied");
-        }
-
-        var session = keyBackupService.newRegistrationSession(basicStorageCredentials, tokenResponse);
-
-        try {
-            var hashedPin = PinHashUtil.hashPin(pin, session.hashSalt());
-            var kbsData = session.restorePin(hashedPin);
-            if (kbsData == null) {
-                throw new AssertionError("Null not expected");
+            case SecureValueRecovery.RestoreResponse.PinMismatch pinMismatch ->
+                    throw new IncorrectPinException(pinMismatch.getTriesRemaining());
+            case SecureValueRecovery.RestoreResponse.ApplicationError error ->
+                    throw new IOException(error.getException());
+            case SecureValueRecovery.RestoreResponse.NetworkError error -> throw error.getException();
+            case SecureValueRecovery.RestoreResponse.Missing missing -> {
+                logger.debug("No SVR data stored for the given credentials.");
+                return null;
             }
-            return kbsData;
-        } catch (UnauthenticatedResponseException | InvalidKeyException e) {
-            throw new IOException(e);
+            case null, default ->
+                    throw new AssertionError("Unexpected response: " + restoreResponse.getClass().getSimpleName());
         }
     }
 }