diff --git a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java index 300371bd07..a0806e3155 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/LiveConfig.java @@ -20,12 +20,15 @@ import okhttp3.Dns; import okhttp3.Interceptor; +import static org.asamk.signal.manager.api.ServiceEnvironment.LIVE; + class LiveConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE = "6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094"; + private static final String SVR2_MRENCLAVE = "a6622ad4656e1abcd0bc0ff17c229477747d2ded0495c4ebee7ed35c1789fa97"; + private static final String SVR2_MRENCLAVE_DEPRECATED = "6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094"; private static final String URL = "https://chat.signal.org"; private static final String CDN_URL = "https://cdn.signal.org"; @@ -69,12 +72,12 @@ static ECPublicKey getUnidentifiedSenderTrustRoot() { } } - static String getCdsiMrenclave() { - return CDSI_MRENCLAVE; - } - - static String getSvr2Mrenclave() { - return SVR2_MRENCLAVE; + static ServiceEnvironmentConfig getServiceEnvironmentConfig(List interceptors) { + return new ServiceEnvironmentConfig(LIVE, + createDefaultServiceConfiguration(interceptors), + getUnidentifiedSenderTrustRoot(), + CDSI_MRENCLAVE, + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_DEPRECATED)); } private LiveConfig() { diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java index 18ff2ce8c7..cd103f801d 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceConfig.java @@ -43,16 +43,8 @@ public static ServiceEnvironmentConfig getServiceEnvironmentConfig( final var interceptors = List.of(userAgentInterceptor); return switch (serviceEnvironment) { - case LIVE -> new ServiceEnvironmentConfig(serviceEnvironment, - LiveConfig.createDefaultServiceConfiguration(interceptors), - LiveConfig.getUnidentifiedSenderTrustRoot(), - LiveConfig.getCdsiMrenclave(), - LiveConfig.getSvr2Mrenclave()); - case STAGING -> new ServiceEnvironmentConfig(serviceEnvironment, - StagingConfig.createDefaultServiceConfiguration(interceptors), - StagingConfig.getUnidentifiedSenderTrustRoot(), - StagingConfig.getCdsiMrenclave(), - StagingConfig.getSvr2Mrenclave()); + case LIVE -> LiveConfig.getServiceEnvironmentConfig(interceptors); + case STAGING -> StagingConfig.getServiceEnvironmentConfig(interceptors); }; } } diff --git a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java index 9664dcae0a..f4622064c9 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/ServiceEnvironmentConfig.java @@ -4,10 +4,12 @@ import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; +import java.util.List; + public record ServiceEnvironmentConfig( ServiceEnvironment type, SignalServiceConfiguration signalServiceConfiguration, ECPublicKey unidentifiedSenderTrustRoot, String cdsiMrenclave, - String svr2Mrenclave + List svr2Mrenclaves ) {} diff --git a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java index f90ae48b15..4409b00547 100644 --- a/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java +++ b/lib/src/main/java/org/asamk/signal/manager/config/StagingConfig.java @@ -20,12 +20,15 @@ import okhttp3.Dns; import okhttp3.Interceptor; +import static org.asamk.signal.manager.api.ServiceEnvironment.STAGING; + class StagingConfig { private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; - private static final String SVR2_MRENCLAVE = "a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95"; + private static final String SVR2_MRENCLAVE = "acb1973aa0bbbd14b3b4e06f145497d948fd4a98efc500fcce363b3b743ec482"; + private static final String SVR2_MRENCLAVE_DEPRECATED = "a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95"; private static final String URL = "https://chat.staging.signal.org"; private static final String CDN_URL = "https://cdn-staging.signal.org"; @@ -69,12 +72,12 @@ static ECPublicKey getUnidentifiedSenderTrustRoot() { } } - static String getCdsiMrenclave() { - return CDSI_MRENCLAVE; - } - - static String getSvr2Mrenclave() { - return SVR2_MRENCLAVE; + static ServiceEnvironmentConfig getServiceEnvironmentConfig(List interceptors) { + return new ServiceEnvironmentConfig(STAGING, + createDefaultServiceConfiguration(interceptors), + getUnidentifiedSenderTrustRoot(), + CDSI_MRENCLAVE, + List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_DEPRECATED)); } private StagingConfig() { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java index 824b278bca..0bcfe09ee3 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/PinHelper.java @@ -5,39 +5,49 @@ import org.slf4j.LoggerFactory; import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.svr.SecureValueRecovery; -import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2; import org.whispersystems.signalservice.internal.push.AuthCredentials; import org.whispersystems.signalservice.internal.push.LockedException; import java.io.IOException; +import java.util.List; public class PinHelper { private static final Logger logger = LoggerFactory.getLogger(PinHelper.class); - private final SecureValueRecoveryV2 secureValueRecoveryV2; + private final List secureValueRecoveries; - public PinHelper(final SecureValueRecoveryV2 secureValueRecoveryV2) { - this.secureValueRecoveryV2 = secureValueRecoveryV2; + public PinHelper(final List secureValueRecoveries) { + this.secureValueRecoveries = secureValueRecoveries; } public void setRegistrationLockPin( String pin, MasterKey masterKey ) throws IOException { - final var backupResponse = secureValueRecoveryV2.setPin(pin, masterKey).execute(); - switch (backupResponse) { - case SecureValueRecovery.BackupResponse.Success success -> { + 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 svr2 failed: ServerRejected"); + case SecureValueRecovery.BackupResponse.EnclaveNotFound enclaveNotFound -> + logger.warn("Backup svr2 failed: EnclaveNotFound"); + case SecureValueRecovery.BackupResponse.ExposeFailure exposeFailure -> + logger.warn("Backup svr2 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; } - case SecureValueRecovery.BackupResponse.ServerRejected serverRejected -> - logger.warn("Backup svr2 failed: ServerRejected"); - case SecureValueRecovery.BackupResponse.EnclaveNotFound enclaveNotFound -> - logger.warn("Backup svr2 failed: EnclaveNotFound"); - case SecureValueRecovery.BackupResponse.ExposeFailure exposeFailure -> - logger.warn("Backup svr2 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"); + } + if (exception != null) { + throw exception; } } @@ -46,27 +56,47 @@ public void migrateRegistrationLockPin(String pin, MasterKey masterKey) throws I } public void removeRegistrationLockPin() throws IOException { - final var deleteResponse = secureValueRecoveryV2.deleteData(); - switch (deleteResponse) { - case SecureValueRecovery.DeleteResponse.Success success -> { + IOException exception = null; + for (final var secureValueRecovery : secureValueRecoveries) { + try { + 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; } - 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"); + } + if (exception != null) { + throw exception; } } public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData( - String pin, LockedException e + String pin, LockedException lockedException ) throws IOException, IncorrectPinException { - var svr2Credentials = e.getSvr2Credentials(); + var svr2Credentials = lockedException.getSvr2Credentials(); if (svr2Credentials != null) { - return getRegistrationLockData(secureValueRecoveryV2, svr2Credentials, pin); + IOException exception = null; + for (final var secureValueRecovery : secureValueRecoveries) { + try { + return getRegistrationLockData(secureValueRecovery, svr2Credentials, pin); + } catch (IOException e) { + exception = e; + } + } + if (exception != null) { + throw exception; + } } return null; diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java index d66ab214c8..ec20096608 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/RegistrationManagerImpl.java @@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException; import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException; +import org.whispersystems.signalservice.api.svr.SecureValueRecovery; import org.whispersystems.signalservice.internal.push.VerifyAccountResponse; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; @@ -90,7 +91,10 @@ public RegistrationManagerImpl( userAgent, groupsV2Operations, ServiceConfig.AUTOMATIC_NETWORK_RETRY); - final var secureValueRecoveryV2 = accountManager.getSecureValueRecoveryV2(serviceEnvironmentConfig.svr2Mrenclave()); + final var secureValueRecoveryV2 = serviceEnvironmentConfig.svr2Mrenclaves() + .stream() + .map(mr -> (SecureValueRecovery) accountManager.getSecureValueRecoveryV2(mr)) + .toList(); this.pinHelper = new PinHelper(secureValueRecoveryV2); } diff --git a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java index 8b5f3e269d..9fb7aff49c 100644 --- a/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java +++ b/lib/src/main/java/org/asamk/signal/manager/internal/SignalDependencies.java @@ -16,13 +16,14 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.services.ProfileService; -import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV2; +import org.whispersystems.signalservice.api.svr.SecureValueRecovery; import org.whispersystems.signalservice.api.util.CredentialsProvider; import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.websocket.WebSocketFactory; import org.whispersystems.signalservice.internal.push.PushServiceSocket; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; +import java.util.List; import java.util.Optional; import java.util.concurrent.ExecutorService; import java.util.function.Supplier; @@ -50,7 +51,7 @@ public class SignalDependencies { private SignalServiceMessageReceiver messageReceiver; private SignalServiceMessageSender messageSender; - private SecureValueRecoveryV2 secureValueRecoveryV2; + private List secureValueRecoveryV2; private ProfileService profileService; private SignalServiceCipher cipher; @@ -192,9 +193,12 @@ public SignalServiceMessageSender getMessageSender() { pushServiceSocket)); } - public SecureValueRecoveryV2 getSecureValueRecoveryV2() { + public List getSecureValueRecoveryV2() { return getOrCreate(() -> secureValueRecoveryV2, - () -> secureValueRecoveryV2 = getAccountManager().getSecureValueRecoveryV2(serviceEnvironmentConfig.svr2Mrenclave())); + () -> secureValueRecoveryV2 = serviceEnvironmentConfig.svr2Mrenclaves() + .stream() + .map(mr -> (SecureValueRecovery) getAccountManager().getSecureValueRecoveryV2(mr)) + .toList()); } public ProfileService getProfileService() {