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());
}
}
}