Use a new SVR2 enclave.

This commit is contained in:
AsamK 2023-12-10 19:30:36 +01:00
parent 2535cd9c2c
commit 4dcdffda0a
7 changed files with 99 additions and 61 deletions

View file

@ -20,12 +20,15 @@ import java.util.Optional;
import okhttp3.Dns; import okhttp3.Dns;
import okhttp3.Interceptor; import okhttp3.Interceptor;
import static org.asamk.signal.manager.api.ServiceEnvironment.LIVE;
class LiveConfig { class LiveConfig {
private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder()
.decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF"); .decode("BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF");
private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; 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 URL = "https://chat.signal.org";
private static final String CDN_URL = "https://cdn.signal.org"; private static final String CDN_URL = "https://cdn.signal.org";
@ -69,12 +72,12 @@ class LiveConfig {
} }
} }
static String getCdsiMrenclave() { static ServiceEnvironmentConfig getServiceEnvironmentConfig(List<Interceptor> interceptors) {
return CDSI_MRENCLAVE; return new ServiceEnvironmentConfig(LIVE,
} createDefaultServiceConfiguration(interceptors),
getUnidentifiedSenderTrustRoot(),
static String getSvr2Mrenclave() { CDSI_MRENCLAVE,
return SVR2_MRENCLAVE; List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_DEPRECATED));
} }
private LiveConfig() { private LiveConfig() {

View file

@ -43,16 +43,8 @@ public class ServiceConfig {
final var interceptors = List.of(userAgentInterceptor); final var interceptors = List.of(userAgentInterceptor);
return switch (serviceEnvironment) { return switch (serviceEnvironment) {
case LIVE -> new ServiceEnvironmentConfig(serviceEnvironment, case LIVE -> LiveConfig.getServiceEnvironmentConfig(interceptors);
LiveConfig.createDefaultServiceConfiguration(interceptors), case STAGING -> StagingConfig.getServiceEnvironmentConfig(interceptors);
LiveConfig.getUnidentifiedSenderTrustRoot(),
LiveConfig.getCdsiMrenclave(),
LiveConfig.getSvr2Mrenclave());
case STAGING -> new ServiceEnvironmentConfig(serviceEnvironment,
StagingConfig.createDefaultServiceConfiguration(interceptors),
StagingConfig.getUnidentifiedSenderTrustRoot(),
StagingConfig.getCdsiMrenclave(),
StagingConfig.getSvr2Mrenclave());
}; };
} }
} }

View file

@ -4,10 +4,12 @@ import org.asamk.signal.manager.api.ServiceEnvironment;
import org.signal.libsignal.protocol.ecc.ECPublicKey; import org.signal.libsignal.protocol.ecc.ECPublicKey;
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration; import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
import java.util.List;
public record ServiceEnvironmentConfig( public record ServiceEnvironmentConfig(
ServiceEnvironment type, ServiceEnvironment type,
SignalServiceConfiguration signalServiceConfiguration, SignalServiceConfiguration signalServiceConfiguration,
ECPublicKey unidentifiedSenderTrustRoot, ECPublicKey unidentifiedSenderTrustRoot,
String cdsiMrenclave, String cdsiMrenclave,
String svr2Mrenclave List<String> svr2Mrenclaves
) {} ) {}

View file

@ -20,12 +20,15 @@ import java.util.Optional;
import okhttp3.Dns; import okhttp3.Dns;
import okhttp3.Interceptor; import okhttp3.Interceptor;
import static org.asamk.signal.manager.api.ServiceEnvironment.STAGING;
class StagingConfig { class StagingConfig {
private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder() private static final byte[] UNIDENTIFIED_SENDER_TRUST_ROOT = Base64.getDecoder()
.decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx"); .decode("BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx");
private static final String CDSI_MRENCLAVE = "0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57"; 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 URL = "https://chat.staging.signal.org";
private static final String CDN_URL = "https://cdn-staging.signal.org"; private static final String CDN_URL = "https://cdn-staging.signal.org";
@ -69,12 +72,12 @@ class StagingConfig {
} }
} }
static String getCdsiMrenclave() { static ServiceEnvironmentConfig getServiceEnvironmentConfig(List<Interceptor> interceptors) {
return CDSI_MRENCLAVE; return new ServiceEnvironmentConfig(STAGING,
} createDefaultServiceConfiguration(interceptors),
getUnidentifiedSenderTrustRoot(),
static String getSvr2Mrenclave() { CDSI_MRENCLAVE,
return SVR2_MRENCLAVE; List.of(SVR2_MRENCLAVE, SVR2_MRENCLAVE_DEPRECATED));
} }
private StagingConfig() { private StagingConfig() {

View file

@ -5,39 +5,49 @@ import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.whispersystems.signalservice.api.kbs.MasterKey; import org.whispersystems.signalservice.api.kbs.MasterKey;
import org.whispersystems.signalservice.api.svr.SecureValueRecovery; 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.AuthCredentials;
import org.whispersystems.signalservice.internal.push.LockedException; import org.whispersystems.signalservice.internal.push.LockedException;
import java.io.IOException; import java.io.IOException;
import java.util.List;
public class PinHelper { public class PinHelper {
private static final Logger logger = LoggerFactory.getLogger(PinHelper.class); private static final Logger logger = LoggerFactory.getLogger(PinHelper.class);
private final SecureValueRecoveryV2 secureValueRecoveryV2; private final List<SecureValueRecovery> secureValueRecoveries;
public PinHelper(final SecureValueRecoveryV2 secureValueRecoveryV2) { public PinHelper(final List<SecureValueRecovery> secureValueRecoveries) {
this.secureValueRecoveryV2 = secureValueRecoveryV2; this.secureValueRecoveries = secureValueRecoveries;
} }
public void setRegistrationLockPin( public void setRegistrationLockPin(
String pin, MasterKey masterKey String pin, MasterKey masterKey
) throws IOException { ) throws IOException {
final var backupResponse = secureValueRecoveryV2.setPin(pin, masterKey).execute(); IOException exception = null;
switch (backupResponse) { for (final var secureValueRecovery : secureValueRecoveries) {
case SecureValueRecovery.BackupResponse.Success success -> { 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"); if (exception != null) {
case SecureValueRecovery.BackupResponse.EnclaveNotFound enclaveNotFound -> throw exception;
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");
} }
} }
@ -46,27 +56,47 @@ public class PinHelper {
} }
public void removeRegistrationLockPin() throws IOException { public void removeRegistrationLockPin() throws IOException {
final var deleteResponse = secureValueRecoveryV2.deleteData(); IOException exception = null;
switch (deleteResponse) { for (final var secureValueRecovery : secureValueRecoveries) {
case SecureValueRecovery.DeleteResponse.Success success -> { 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"); if (exception != null) {
case SecureValueRecovery.DeleteResponse.EnclaveNotFound enclaveNotFound -> throw exception;
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");
} }
} }
public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData( public SecureValueRecovery.RestoreResponse.Success getRegistrationLockData(
String pin, LockedException e String pin, LockedException lockedException
) throws IOException, IncorrectPinException { ) throws IOException, IncorrectPinException {
var svr2Credentials = e.getSvr2Credentials(); var svr2Credentials = lockedException.getSvr2Credentials();
if (svr2Credentials != null) { 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; return null;

View file

@ -45,6 +45,7 @@ import org.whispersystems.signalservice.api.push.ServiceIdType;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException; import org.whispersystems.signalservice.api.push.exceptions.AlreadyVerifiedException;
import org.whispersystems.signalservice.api.push.exceptions.DeprecatedVersionException; 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.push.VerifyAccountResponse;
import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider; import org.whispersystems.signalservice.internal.util.DynamicCredentialsProvider;
@ -90,7 +91,10 @@ public class RegistrationManagerImpl implements RegistrationManager {
userAgent, userAgent,
groupsV2Operations, groupsV2Operations,
ServiceConfig.AUTOMATIC_NETWORK_RETRY); 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); this.pinHelper = new PinHelper(secureValueRecoveryV2);
} }

View file

@ -16,13 +16,14 @@ import org.whispersystems.signalservice.api.groupsv2.GroupsV2Api;
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations; import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
import org.whispersystems.signalservice.api.push.SignalServiceAddress; import org.whispersystems.signalservice.api.push.SignalServiceAddress;
import org.whispersystems.signalservice.api.services.ProfileService; 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.CredentialsProvider;
import org.whispersystems.signalservice.api.util.UptimeSleepTimer; import org.whispersystems.signalservice.api.util.UptimeSleepTimer;
import org.whispersystems.signalservice.api.websocket.WebSocketFactory; import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
import org.whispersystems.signalservice.internal.push.PushServiceSocket; import org.whispersystems.signalservice.internal.push.PushServiceSocket;
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection; import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -50,7 +51,7 @@ public class SignalDependencies {
private SignalServiceMessageReceiver messageReceiver; private SignalServiceMessageReceiver messageReceiver;
private SignalServiceMessageSender messageSender; private SignalServiceMessageSender messageSender;
private SecureValueRecoveryV2 secureValueRecoveryV2; private List<SecureValueRecovery> secureValueRecoveryV2;
private ProfileService profileService; private ProfileService profileService;
private SignalServiceCipher cipher; private SignalServiceCipher cipher;
@ -192,9 +193,12 @@ public class SignalDependencies {
pushServiceSocket)); pushServiceSocket));
} }
public SecureValueRecoveryV2 getSecureValueRecoveryV2() { public List<SecureValueRecovery> getSecureValueRecoveryV2() {
return getOrCreate(() -> secureValueRecoveryV2, return getOrCreate(() -> secureValueRecoveryV2,
() -> secureValueRecoveryV2 = getAccountManager().getSecureValueRecoveryV2(serviceEnvironmentConfig.svr2Mrenclave())); () -> secureValueRecoveryV2 = serviceEnvironmentConfig.svr2Mrenclaves()
.stream()
.map(mr -> (SecureValueRecovery) getAccountManager().getSecureValueRecoveryV2(mr))
.toList());
} }
public ProfileService getProfileService() { public ProfileService getProfileService() {