Remove all unused KBS/SVR1 code.
This commit is contained in:
parent
5b0e71b680
commit
609e9fcdb0
24 changed files with 8 additions and 840 deletions
|
@ -186,7 +186,6 @@ android {
|
|||
buildConfigField "String[]", "SIGNAL_CDN_IPS", cdn_ips
|
||||
buildConfigField "String[]", "SIGNAL_CDN2_IPS", cdn2_ips
|
||||
buildConfigField "String[]", "SIGNAL_CDN3_IPS", cdn3_ips
|
||||
buildConfigField "String[]", "SIGNAL_KBS_IPS", kbs_ips
|
||||
buildConfigField "String[]", "SIGNAL_SFU_IPS", sfu_ips
|
||||
buildConfigField "String[]", "SIGNAL_CONTENT_PROXY_IPS", content_proxy_ips
|
||||
buildConfigField "String[]", "SIGNAL_CDSI_IPS", cdsi_ips
|
||||
|
@ -194,12 +193,6 @@ android {
|
|||
buildConfigField "String", "SIGNAL_AGENT", "\"OWA\""
|
||||
buildConfigField "String", "CDSI_MRENCLAVE", "\"0f6fd79cdfdaa5b2e6337f534d3baf999318b0c462a7ac1f41297a3e4b424a57\""
|
||||
buildConfigField "String", "SVR2_MRENCLAVE", "\"6ee1042f9e20f880326686dd4ba50c25359f01e9f733eeba4382bca001d45094\""
|
||||
buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"e18376436159cda3ad7a45d9320e382e4a497f26b0dca34d8eab0bd0139483b5\", " +
|
||||
"\"3a485adb56e2058ef7737764c738c4069dd62bc457637eafb6bbce1ce29ddb89\", " +
|
||||
"\"45627094b2ea4a66f4cf0b182858a8dcf4b8479122c3820fe7fd0551a6d4cf5c\")"
|
||||
buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[] { new org.thoughtcrime.securesms.KbsEnclave(\"0cedba03535b41b67729ce9924185f831d7767928a1d1689acb689bc079c375f\", " +
|
||||
"\"187d2739d22be65e74b65f0055e74d31310e4267e5fac2b1246cc8beba81af39\", " +
|
||||
"\"ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba\") }"
|
||||
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BXu6QIKVz5MA8gstzfOgRQGqyLqOwNKHL6INkv3IHWMF\""
|
||||
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"AMhf5ywVwITZMsff/eCyudZx9JDmkkkbV6PInzG4p8x3VqVJSFiMvnvlEKWuRob/1eaIetR31IYeAbm0NdOuHH8Qi+Rexi1wLlpzIo1gstHWBfZzy1+qHRV5A4TqPp15YzBPm0WSggW6PbSn+F4lf57VCnHF7p8SvzAA2ZZJPYJURt8X7bbg+H3i+PEjH9DXItNEqs2sNcug37xZQDLm7X36nOoGPs54XsEGzPdEV+itQNGUFEjY6X9Uv+Acuks7NpyGvCoKxGwgKgE5XyJ+nNKlyHHOLb6N1NuHyBrZrgtY/JYJHRooo5CEqYKBqdFnmbTVGEkCvJKxLnjwKWf+fEPoWeQFj5ObDjcKMZf2Jm2Ae69x+ikU5gBXsRmoF94GXTLfN0/vLt98KDPnxwAQL9j5V1jGOY8jQl6MLxEs56cwXN0dqCnImzVH3TZT1cJ8SW1BRX6qIVxEzjsSGx3yxF3suAilPMqGRp4ffyopjMD1JXiKR2RwLKzizUe5e8XyGOy9fplzhw3jVzTRyUZTRSZKkMLWcQ/gv0E4aONNqs4P\""
|
||||
buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AByD873dTilmOSG0TjKrvpeaKEsUmIO8Vx9BeMmftwUs9v7ikPwM8P3OHyT0+X3EUMZrSe9VUp26Wai51Q9I8mdk0hX/yo7CeFGJyzoOqn8e/i4Ygbn5HoAyXJx5eXfIbqpc0bIxzju4H/HOQeOpt6h742qii5u/cbwOhFZCsMIbElZTaeU+BWMBQiZHIGHT5IE0qCordQKZ5iPZom0HeFa8Yq0ShuEyAl0WINBiY6xE3H/9WnvzXBbMuuk//eRxXgzO8ieCeK8FwQNxbfXqZm6Ro1cMhCOF3u7xoX83QhpN\""
|
||||
|
@ -373,12 +366,6 @@ android {
|
|||
buildConfigField "String", "SIGNAL_KEY_BACKUP_URL", "\"https://api-staging.backup.signal.org\""
|
||||
buildConfigField "String", "SIGNAL_SVR2_URL", "\"https://svr2.staging.signal.org\""
|
||||
buildConfigField "String", "SVR2_MRENCLAVE", "\"a8a261420a6bb9b61aa25bf8a79e8bd20d7652531feb3381cbffd446d270be95\""
|
||||
buildConfigField "org.thoughtcrime.securesms.KbsEnclave", "KBS_ENCLAVE", "new org.thoughtcrime.securesms.KbsEnclave(\"39963b736823d5780be96ab174869a9499d56d66497aa8f9b2244f777ebc366b\", " +
|
||||
"\"ee1d0d972b7ea903615670de43ab1b6e7a825e811c70a29bb5fe0f819e0975fa\", " +
|
||||
"\"45627094b2ea4a66f4cf0b182858a8dcf4b8479122c3820fe7fd0551a6d4cf5c\")"
|
||||
buildConfigField "org.thoughtcrime.securesms.KbsEnclave[]", "KBS_FALLBACKS", "new org.thoughtcrime.securesms.KbsEnclave[] { new org.thoughtcrime.securesms.KbsEnclave(\"dd6f66d397d9e8cf6ec6db238e59a7be078dd50e9715427b9c89b409ffe53f99\", " +
|
||||
"\"4200003414528c151e2dccafbc87aa6d3d66a5eb8f8c05979a6e97cb33cd493a\", " +
|
||||
"\"ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba\") }"
|
||||
buildConfigField "String", "UNIDENTIFIED_SENDER_TRUST_ROOT", "\"BbqY1DzohE4NUZoVF+L18oUPrK3kILllLEJh2UnPSsEx\""
|
||||
buildConfigField "String", "ZKGROUP_SERVER_PUBLIC_PARAMS", "\"ABSY21VckQcbSXVNCGRYJcfWHiAMZmpTtTELcDmxgdFbtp/bWsSxZdMKzfCp8rvIs8ocCU3B37fT3r4Mi5qAemeGeR2X+/YmOGR5ofui7tD5mDQfstAI9i+4WpMtIe8KC3wU5w3Inq3uNWVmoGtpKndsNfwJrCg0Hd9zmObhypUnSkfYn2ooMOOnBpfdanRtrvetZUayDMSC5iSRcXKpdlukrpzzsCIvEwjwQlJYVPOQPj4V0F4UXXBdHSLK05uoPBCQG8G9rYIGedYsClJXnbrgGYG3eMTG5hnx4X4ntARBgELuMWWUEEfSK0mjXg+/2lPmWcTZWR9nkqgQQP0tbzuiPm74H2wMO4u1Wafe+UwyIlIT9L7KLS19Aw8r4sPrXZSSsOZ6s7M1+rTJN0bI5CKY2PX29y5Ok3jSWufIKcgKOnWoP67d5b2du2ZVJjpjfibNIHbT/cegy/sBLoFwtHogVYUewANUAXIaMPyCLRArsKhfJ5wBtTminG/PAvuBdJ70Z/bXVPf8TVsR292zQ65xwvWTejROW6AZX6aqucUj\""
|
||||
buildConfigField "String", "GENERIC_SERVER_PUBLIC_PARAMS", "\"AHILOIrFPXX9laLbalbA9+L1CXpSbM/bTJXZGZiuyK1JaI6dK5FHHWL6tWxmHKYAZTSYmElmJ5z2A5YcirjO/yfoemE03FItyaf8W1fE4p14hzb5qnrmfXUSiAIVrhaXVwIwSzH6RL/+EO8jFIjJ/YfExfJ8aBl48CKHgu1+A6kWynhttonvWWx6h7924mIzW0Czj2ROuh4LwQyZypex4GuOPW8sgIT21KNZaafgg+KbV7XM1x1tF3XA17B4uGUaDbDw2O+nR1+U5p6qHPzmJ7ggFjSN6Utu+35dS1sS0P9N\""
|
||||
|
|
|
@ -230,8 +230,6 @@ class ChangeNumberViewModelTest {
|
|||
lateinit var changeNumberRequest: ChangePhoneNumberRequest
|
||||
lateinit var setPreKeysRequest: PreKeyState
|
||||
|
||||
MockProvider.mockGetRegistrationLockStringFlow()
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
|
||||
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
|
||||
|
@ -318,8 +316,6 @@ class ChangeNumberViewModelTest {
|
|||
lateinit var changeNumberRequest: ChangePhoneNumberRequest
|
||||
lateinit var setPreKeysRequest: PreKeyState
|
||||
|
||||
MockProvider.mockGetRegistrationLockStringFlow()
|
||||
|
||||
InstrumentationApplicationDependencyProvider.addMockWebRequestHandlers(
|
||||
Post("/v1/verification/session") { MockResponse().success(MockProvider.sessionMetadataJson.copy(verified = false)) },
|
||||
Put("/v1/verification/session/${MockProvider.sessionMetadataJson.id}/code") { MockResponse().success(MockProvider.sessionMetadataJson) },
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.mockito.kotlin.mock
|
|||
import org.signal.core.util.Base64
|
||||
import org.signal.core.util.logging.Log
|
||||
import org.thoughtcrime.securesms.BuildConfig
|
||||
import org.thoughtcrime.securesms.KbsEnclave
|
||||
import org.thoughtcrime.securesms.push.SignalServiceNetworkAccess
|
||||
import org.thoughtcrime.securesms.push.SignalServiceTrustStore
|
||||
import org.thoughtcrime.securesms.recipients.LiveRecipientCache
|
||||
|
@ -24,31 +23,25 @@ import org.thoughtcrime.securesms.testing.Get
|
|||
import org.thoughtcrime.securesms.testing.Verb
|
||||
import org.thoughtcrime.securesms.testing.runSync
|
||||
import org.thoughtcrime.securesms.testing.success
|
||||
import org.whispersystems.signalservice.api.KeyBackupService
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager
|
||||
import org.whispersystems.signalservice.api.push.TrustStore
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalCdnUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalCdsiUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalKeyBackupServiceUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalStorageUrl
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalSvr2Url
|
||||
import java.security.KeyStore
|
||||
import java.util.Optional
|
||||
|
||||
/**
|
||||
* Dependency provider used for instrumentation tests (aka androidTests).
|
||||
*
|
||||
* Handles setting up a mock web server for API calls, and provides mockable versions of [SignalServiceNetworkAccess] and
|
||||
* [KeyBackupService].
|
||||
* Handles setting up a mock web server for API calls, and provides mockable versions of [SignalServiceNetworkAccess].
|
||||
*/
|
||||
class InstrumentationApplicationDependencyProvider(application: Application, default: ApplicationDependencyProvider) : ApplicationDependencies.Provider by default {
|
||||
|
||||
private val serviceTrustStore: TrustStore
|
||||
private val uncensoredConfiguration: SignalServiceConfiguration
|
||||
private val serviceNetworkAccessMock: SignalServiceNetworkAccess
|
||||
private val keyBackupService: KeyBackupService
|
||||
private val recipientCache: LiveRecipientCache
|
||||
|
||||
init {
|
||||
|
@ -80,7 +73,6 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
|
|||
0 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||
2 to arrayOf(SignalCdnUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT))
|
||||
),
|
||||
signalKeyBackupServiceUrls = arrayOf(SignalKeyBackupServiceUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||
signalStorageUrls = arrayOf(SignalStorageUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||
signalCdsiUrls = arrayOf(SignalCdsiUrl(baseUrl, "localhost", serviceTrustStore, ConnectionSpec.CLEARTEXT)),
|
||||
signalSvr2Urls = arrayOf(SignalSvr2Url(baseUrl, serviceTrustStore, "localhost", ConnectionSpec.CLEARTEXT)),
|
||||
|
@ -97,8 +89,6 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
|
|||
on { uncensoredConfiguration } doReturn uncensoredConfiguration
|
||||
}
|
||||
|
||||
keyBackupService = mock()
|
||||
|
||||
recipientCache = LiveRecipientCache(application) { r -> r.run() }
|
||||
}
|
||||
|
||||
|
@ -106,10 +96,6 @@ class InstrumentationApplicationDependencyProvider(application: Application, def
|
|||
return serviceNetworkAccessMock
|
||||
}
|
||||
|
||||
override fun provideKeyBackupService(signalServiceAccountManager: SignalServiceAccountManager, keyStore: KeyStore, enclave: KbsEnclave): KeyBackupService {
|
||||
return keyBackupService
|
||||
}
|
||||
|
||||
override fun provideRecipientCache(): LiveRecipientCache {
|
||||
return recipientCache
|
||||
}
|
||||
|
|
|
@ -1,22 +1,12 @@
|
|||
package org.thoughtcrime.securesms.testing
|
||||
|
||||
import org.mockito.kotlin.anyOrNull
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.stub
|
||||
import org.signal.core.util.Hex
|
||||
import org.signal.libsignal.protocol.IdentityKeyPair
|
||||
import org.signal.libsignal.protocol.ecc.Curve
|
||||
import org.signal.libsignal.protocol.state.PreKeyRecord
|
||||
import org.signal.libsignal.protocol.util.KeyHelper
|
||||
import org.signal.libsignal.protocol.util.Medium
|
||||
import org.signal.libsignal.svr2.PinHash
|
||||
import org.thoughtcrime.securesms.crypto.PreKeyUtil
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.thoughtcrime.securesms.test.BuildConfig
|
||||
import org.whispersystems.signalservice.api.KeyBackupService
|
||||
import org.whispersystems.signalservice.api.SvrPinData
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.api.messages.multidevice.DeviceInfo
|
||||
import org.whispersystems.signalservice.api.push.ServiceId
|
||||
import org.whispersystems.signalservice.api.push.SignedPreKeyEntity
|
||||
|
@ -78,18 +68,6 @@ object MockProvider {
|
|||
}
|
||||
}
|
||||
|
||||
fun mockGetRegistrationLockStringFlow() {
|
||||
val session: KeyBackupService.RestoreSession = object : KeyBackupService.RestoreSession {
|
||||
override fun hashSalt(): ByteArray = Hex.fromStringCondensed("cba811749042b303a6a7efa5ccd160aea5e3ea243c8d2692bd13d515732f51a8")
|
||||
override fun restorePin(hashedPin: PinHash?): SvrPinData = SvrPinData(MasterKey.createNew(SecureRandom()), null)
|
||||
}
|
||||
|
||||
val kbsService = ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE)
|
||||
kbsService.stub {
|
||||
on { newRegistrationSession(anyOrNull(), anyOrNull()) } doReturn session
|
||||
}
|
||||
}
|
||||
|
||||
fun createPreKeyResponse(identity: IdentityKeyPair = SignalStore.account().aciIdentityKey, deviceId: Int): PreKeyResponse {
|
||||
val signedPreKeyRecord = PreKeyUtil.generateSignedPreKey(SecureRandom().nextInt(Medium.MAX_VALUE), identity.privateKey)
|
||||
val oneTimePreKey = PreKeyRecord(SecureRandom().nextInt(Medium.MAX_VALUE), Curve.generateKeyPair())
|
||||
|
|
|
@ -124,7 +124,7 @@ fun SvrPlaygroundScreenLightTheme() {
|
|||
Surface {
|
||||
SvrPlaygroundScreen(
|
||||
state = InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
options = persistentListOf(SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
@ -138,7 +138,7 @@ fun SvrPlaygroundScreenDarkTheme() {
|
|||
Surface {
|
||||
SvrPlaygroundScreen(
|
||||
state = InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
options = persistentListOf(SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -13,5 +13,5 @@ data class InternalSvrPlaygroundState(
|
|||
enum class SvrImplementation(
|
||||
val title: String
|
||||
) {
|
||||
SVR1("KBS"), SVR2("SVR2")
|
||||
SVR2("SVR2")
|
||||
}
|
||||
|
|
|
@ -19,13 +19,12 @@ import org.thoughtcrime.securesms.BuildConfig
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV1
|
||||
|
||||
class InternalSvrPlaygroundViewModel : ViewModel() {
|
||||
|
||||
private val _state: MutableState<InternalSvrPlaygroundState> = mutableStateOf(
|
||||
InternalSvrPlaygroundState(
|
||||
options = persistentListOf(SvrImplementation.SVR1, SvrImplementation.SVR2)
|
||||
options = persistentListOf(SvrImplementation.SVR2)
|
||||
)
|
||||
)
|
||||
val state: State<InternalSvrPlaygroundState> = _state
|
||||
|
@ -104,7 +103,6 @@ class InternalSvrPlaygroundViewModel : ViewModel() {
|
|||
|
||||
private fun SvrImplementation.toImplementation(): SecureValueRecovery {
|
||||
return when (this) {
|
||||
SvrImplementation.SVR1 -> SecureValueRecoveryV1(ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE))
|
||||
SvrImplementation.SVR2 -> ApplicationDependencies.getSignalServiceAccountManager().getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,13 +14,11 @@ import org.thoughtcrime.securesms.database.model.GroupRecord;
|
|||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
import org.thoughtcrime.securesms.pin.KbsEnclaves;
|
||||
import org.thoughtcrime.securesms.subscription.Subscriber;
|
||||
import org.thoughtcrime.securesms.util.ServiceUtil;
|
||||
import org.whispersystems.signalservice.api.util.PhoneNumberFormatter;
|
||||
import org.whispersystems.signalservice.internal.EmptyResponse;
|
||||
import org.whispersystems.signalservice.internal.ServiceResponse;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.Collator;
|
||||
|
|
|
@ -10,7 +10,6 @@ import androidx.annotation.VisibleForTesting;
|
|||
import org.signal.core.util.concurrent.DeadlockDetector;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl;
|
||||
|
@ -41,12 +40,10 @@ import org.thoughtcrime.securesms.shakereport.ShakeToReport;
|
|||
import org.thoughtcrime.securesms.util.AppForegroundObserver;
|
||||
import org.thoughtcrime.securesms.util.EarlyMessageCache;
|
||||
import org.thoughtcrime.securesms.util.FrameRateTracker;
|
||||
import org.thoughtcrime.securesms.util.IasKeyStore;
|
||||
import org.thoughtcrime.securesms.video.exo.ExoPlayerPool;
|
||||
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache;
|
||||
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool;
|
||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat;
|
||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.SignalServiceDataStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
@ -63,7 +60,6 @@ import org.whispersystems.signalservice.internal.util.BlacklistingTrustManager;
|
|||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
|
||||
import java.security.KeyManagementException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -199,10 +195,6 @@ public class ApplicationDependencies {
|
|||
return groupsV2Operations;
|
||||
}
|
||||
|
||||
public static @NonNull KeyBackupService getKeyBackupService(@NonNull KbsEnclave enclave) {
|
||||
return provider.provideKeyBackupService(getSignalServiceAccountManager(), IasKeyStore.getIasKeyStore(application), enclave);
|
||||
}
|
||||
|
||||
public static @NonNull GroupsV2StateProcessor getGroupsV2StateProcessor() {
|
||||
if (groupsV2StateProcessor == null) {
|
||||
synchronized (LOCK) {
|
||||
|
@ -724,7 +716,6 @@ public class ApplicationDependencies {
|
|||
@NonNull ProfileService provideProfileService(@NonNull ClientZkProfileOperations profileOperations, @NonNull SignalServiceMessageReceiver signalServiceMessageReceiver, @NonNull SignalWebSocket signalWebSocket);
|
||||
@NonNull DeadlockDetector provideDeadlockDetector();
|
||||
@NonNull ClientZkReceiptOperations provideClientZkReceiptOperations(@NonNull SignalServiceConfiguration signalServiceConfiguration);
|
||||
@NonNull KeyBackupService provideKeyBackupService(@NonNull SignalServiceAccountManager signalServiceAccountManager, @NonNull KeyStore keyStore, @NonNull KbsEnclave enclave);
|
||||
@NonNull ScheduledMessageManager provideScheduledMessageManager();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,14 +8,12 @@ import android.os.HandlerThread;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import org.signal.core.util.Hex;
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.signal.core.util.concurrent.DeadlockDetector;
|
||||
import org.signal.core.util.concurrent.SignalExecutors;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.ReentrantSessionLock;
|
||||
|
@ -74,7 +72,6 @@ import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
|||
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache;
|
||||
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool;
|
||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat;
|
||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.SignalServiceDataStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
@ -94,7 +91,6 @@ import org.whispersystems.signalservice.api.websocket.WebSocketFactory;
|
|||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||
import org.whispersystems.signalservice.internal.websocket.WebSocketConnection;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Supplier;
|
||||
|
@ -394,15 +390,6 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
|||
return provideClientZkOperations(signalServiceConfiguration).getReceiptOperations();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull KeyBackupService provideKeyBackupService(@NonNull SignalServiceAccountManager signalServiceAccountManager, @NonNull KeyStore keyStore, @NonNull KbsEnclave enclave) {
|
||||
return signalServiceAccountManager.getKeyBackupService(keyStore,
|
||||
enclave.getEnclaveName(),
|
||||
Hex.fromStringOrThrow(enclave.getServiceId()),
|
||||
enclave.getMrEnclave(),
|
||||
10);
|
||||
}
|
||||
|
||||
@NonNull WebSocketFactory provideWebSocketFactory(@NonNull Supplier<SignalServiceConfiguration> signalServiceConfigurationSupplier, @NonNull SignalWebSocketHealthMonitor healthMonitor) {
|
||||
return new WebSocketFactory() {
|
||||
@Override
|
||||
|
|
|
@ -16,7 +16,6 @@ import org.whispersystems.signalservice.api.kbs.MasterKey
|
|||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.BackupResponse
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSession
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV1
|
||||
import kotlin.concurrent.withLock
|
||||
import kotlin.time.Duration.Companion.days
|
||||
|
||||
|
@ -83,14 +82,7 @@ class ResetSvrGuessCountJob private constructor(
|
|||
Result.success()
|
||||
}
|
||||
|
||||
if (!svr2Result.isSuccess) {
|
||||
return svr2Result
|
||||
} else {
|
||||
Log.d(TAG, "SVR2 reset complete. Marking as such so we do not retry it if SVR1 fails.")
|
||||
svr2Complete = true
|
||||
}
|
||||
|
||||
return resetGuessCount(SecureValueRecoveryV1(ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE)), pin, masterKey)
|
||||
return svr2Result
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
package org.thoughtcrime.securesms.pin;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import org.thoughtcrime.securesms.BuildConfig;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.util.Util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public final class KbsEnclaves {
|
||||
|
||||
public static @NonNull KbsEnclave current() {
|
||||
return BuildConfig.KBS_ENCLAVE;
|
||||
}
|
||||
|
||||
public static @NonNull List<KbsEnclave> all() {
|
||||
return Util.join(Collections.singletonList(BuildConfig.KBS_ENCLAVE), fallbacks());
|
||||
}
|
||||
|
||||
public static @NonNull List<KbsEnclave> fallbacks() {
|
||||
return Arrays.asList(BuildConfig.KBS_FALLBACKS);
|
||||
}
|
||||
}
|
|
@ -29,7 +29,6 @@ import org.whispersystems.signalservice.api.kbs.MasterKey
|
|||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.BackupResponse
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.RestoreResponse
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecoveryV1
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
import java.io.IOException
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
@ -41,10 +40,9 @@ object SvrRepository {
|
|||
val TAG = Log.tag(SvrRepository::class.java)
|
||||
|
||||
private val svr2: SecureValueRecovery = ApplicationDependencies.getSignalServiceAccountManager().getSecureValueRecoveryV2(BuildConfig.SVR2_MRENCLAVE)
|
||||
private val svr1: SecureValueRecovery = SecureValueRecoveryV1(ApplicationDependencies.getKeyBackupService(BuildConfig.KBS_ENCLAVE))
|
||||
|
||||
/** An ordered list of SVR implementations. They should be in priority order, with the most important one listed first. */
|
||||
private val implementations: List<SecureValueRecovery> = listOf(svr2, svr1)
|
||||
private val implementations: List<SecureValueRecovery> = listOf(svr2)
|
||||
|
||||
/**
|
||||
* A lock that ensures that only one thread at a time is altering the various pieces of SVR state.
|
||||
|
@ -72,8 +70,7 @@ object SvrRepository {
|
|||
Log.i(TAG, "restoreMasterKeyPreRegistration()", true)
|
||||
|
||||
val operations: List<Pair<SecureValueRecovery, () -> RestoreResponse>> = listOf(
|
||||
svr2 to { restoreMasterKeyPreRegistration(svr2, credentials.svr2, userPin) },
|
||||
svr1 to { restoreMasterKeyPreRegistration(svr1, credentials.svr1, userPin) }
|
||||
svr2 to { restoreMasterKeyPreRegistration(svr2, credentials.svr2, userPin) }
|
||||
)
|
||||
|
||||
for ((implementation, operation) in operations) {
|
||||
|
|
|
@ -48,7 +48,6 @@ open class SignalServiceNetworkAccess(context: Context) {
|
|||
BuildConfig.STORAGE_URL.stripProtocol() to BuildConfig.SIGNAL_STORAGE_IPS.toSet(),
|
||||
BuildConfig.SIGNAL_CDN_URL.stripProtocol() to BuildConfig.SIGNAL_CDN_IPS.toSet(),
|
||||
BuildConfig.SIGNAL_CDN2_URL.stripProtocol() to BuildConfig.SIGNAL_CDN2_IPS.toSet(),
|
||||
BuildConfig.SIGNAL_KEY_BACKUP_URL.stripProtocol() to BuildConfig.SIGNAL_KBS_IPS.toSet(),
|
||||
BuildConfig.SIGNAL_SFU_URL.stripProtocol() to BuildConfig.SIGNAL_SFU_IPS.toSet(),
|
||||
BuildConfig.CONTENT_PROXY_HOST.stripProtocol() to BuildConfig.SIGNAL_CONTENT_PROXY_IPS.toSet(),
|
||||
BuildConfig.SIGNAL_CDSI_URL.stripProtocol() to BuildConfig.SIGNAL_CDSI_IPS.toSet(),
|
||||
|
@ -176,7 +175,6 @@ open class SignalServiceNetworkAccess(context: Context) {
|
|||
2 to fUrls.map { SignalCdnUrl(it, F_CDN2_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||
3 to fUrls.map { SignalCdnUrl(it, F_CDN3_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray()
|
||||
),
|
||||
signalKeyBackupServiceUrls = fUrls.map { SignalKeyBackupServiceUrl(it, F_KBS_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||
signalStorageUrls = fUrls.map { SignalStorageUrl(it, F_STORAGE_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||
signalCdsiUrls = fUrls.map { SignalCdsiUrl(it, F_CDSI_HOST, fTrustStore, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||
signalSvr2Urls = fUrls.map { SignalSvr2Url(it, fTrustStore, F_SVR2_HOST, APP_CONNECTION_SPEC) }.toTypedArray(),
|
||||
|
@ -229,7 +227,6 @@ open class SignalServiceNetworkAccess(context: Context) {
|
|||
2 to arrayOf(SignalCdnUrl(BuildConfig.SIGNAL_CDN2_URL, serviceTrustStore)),
|
||||
3 to arrayOf(SignalCdnUrl(BuildConfig.SIGNAL_CDN3_URL, serviceTrustStore))
|
||||
),
|
||||
signalKeyBackupServiceUrls = arrayOf(SignalKeyBackupServiceUrl(BuildConfig.SIGNAL_KEY_BACKUP_URL, serviceTrustStore)),
|
||||
signalStorageUrls = arrayOf(SignalStorageUrl(BuildConfig.STORAGE_URL, serviceTrustStore)),
|
||||
signalCdsiUrls = arrayOf(SignalCdsiUrl(BuildConfig.SIGNAL_CDSI_URL, serviceTrustStore)),
|
||||
signalSvr2Urls = arrayOf(SignalSvr2Url(BuildConfig.SIGNAL_SVR2_URL, serviceTrustStore)),
|
||||
|
@ -299,7 +296,6 @@ open class SignalServiceNetworkAccess(context: Context) {
|
|||
2 to cdn2Urls,
|
||||
3 to cdn3Urls
|
||||
),
|
||||
signalKeyBackupServiceUrls = kbsUrls,
|
||||
signalStorageUrls = storageUrls,
|
||||
signalCdsiUrls = cdsiUrls,
|
||||
signalSvr2Urls = svr2Urls,
|
||||
|
|
|
@ -5,7 +5,6 @@ import androidx.annotation.NonNull;
|
|||
import org.signal.core.util.concurrent.DeadlockDetector;
|
||||
import org.signal.libsignal.zkgroup.profiles.ClientZkProfileOperations;
|
||||
import org.signal.libsignal.zkgroup.receipts.ClientZkReceiptOperations;
|
||||
import org.thoughtcrime.securesms.KbsEnclave;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusRepository;
|
||||
import org.thoughtcrime.securesms.components.TypingStatusSender;
|
||||
import org.thoughtcrime.securesms.crypto.storage.SignalServiceDataStoreImpl;
|
||||
|
@ -33,7 +32,6 @@ import org.thoughtcrime.securesms.util.FrameRateTracker;
|
|||
import org.thoughtcrime.securesms.video.exo.GiphyMp4Cache;
|
||||
import org.thoughtcrime.securesms.video.exo.SimpleExoPlayerPool;
|
||||
import org.thoughtcrime.securesms.webrtc.audio.AudioManagerCompat;
|
||||
import org.whispersystems.signalservice.api.KeyBackupService;
|
||||
import org.whispersystems.signalservice.api.SignalServiceAccountManager;
|
||||
import org.whispersystems.signalservice.api.SignalServiceDataStore;
|
||||
import org.whispersystems.signalservice.api.SignalServiceMessageReceiver;
|
||||
|
@ -45,7 +43,6 @@ import org.whispersystems.signalservice.api.services.DonationsService;
|
|||
import org.whispersystems.signalservice.api.services.ProfileService;
|
||||
import org.whispersystems.signalservice.internal.configuration.SignalServiceConfiguration;
|
||||
|
||||
import java.security.KeyStore;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
@ -231,11 +228,6 @@ public class MockApplicationDependencyProvider implements ApplicationDependencie
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull KeyBackupService provideKeyBackupService(@NonNull SignalServiceAccountManager signalServiceAccountManager, @NonNull KeyStore keyStore, @NonNull KbsEnclave enclave) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull ScheduledMessageManager provideScheduledMessageManager() {
|
||||
return null;
|
||||
|
|
|
@ -3,7 +3,6 @@ ext.storage_ips='new String[]{"142.250.81.243"}'
|
|||
ext.cdn_ips='new String[]{"18.238.55.2","18.238.55.54","18.238.55.7","18.238.55.78"}'
|
||||
ext.cdn2_ips='new String[]{"104.18.37.148","172.64.150.108"}'
|
||||
ext.cdn3_ips='new String[]{"104.18.37.148","172.64.150.108"}'
|
||||
ext.kbs_ips='new String[]{"18.238.4.110","18.238.4.119","18.238.4.19","18.238.4.94"}'
|
||||
ext.sfu_ips='new String[]{"34.95.127.167"}'
|
||||
ext.content_proxy_ips='new String[]{"107.178.250.75"}'
|
||||
ext.svr2_ips='new String[]{"20.119.62.85"}'
|
||||
|
|
|
@ -131,7 +131,6 @@ task resolveStaticIps {
|
|||
ext.cdn_ips='${staticIpResolver.resolveToBuildConfig("cdn.signal.org")}'
|
||||
ext.cdn2_ips='${staticIpResolver.resolveToBuildConfig("cdn2.signal.org")}'
|
||||
ext.cdn3_ips='${staticIpResolver.resolveToBuildConfig("cdn3.signal.org")}'
|
||||
ext.kbs_ips='${staticIpResolver.resolveToBuildConfig("api.backup.signal.org")}'
|
||||
ext.sfu_ips='${staticIpResolver.resolveToBuildConfig("sfu.voip.signal.org")}'
|
||||
ext.content_proxy_ips='${staticIpResolver.resolveToBuildConfig("contentproxy.signal.org")}'
|
||||
ext.svr2_ips='${staticIpResolver.resolveToBuildConfig("svr2.signal.org")}'
|
||||
|
|
|
@ -1,303 +0,0 @@
|
|||
package org.whispersystems.signalservice.api;
|
||||
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.logging.Log;
|
||||
import org.signal.libsignal.svr2.PinHash;
|
||||
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
|
||||
import org.whispersystems.signalservice.api.kbs.KbsData;
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey;
|
||||
import org.whispersystems.signalservice.api.kbs.PinHashUtil;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.KeyBackupCipher;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestation;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.KeyBackupRequest;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.KeyBackupResponse;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
import org.whispersystems.signalservice.internal.keybackup.protos.BackupResponse;
|
||||
import org.whispersystems.signalservice.internal.keybackup.protos.RestoreResponse;
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials;
|
||||
import org.whispersystems.signalservice.internal.push.PushServiceSocket;
|
||||
import org.whispersystems.signalservice.internal.push.RemoteAttestationUtil;
|
||||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Locale;
|
||||
|
||||
public class KeyBackupService {
|
||||
|
||||
private static final String TAG = KeyBackupService.class.getSimpleName();
|
||||
|
||||
private final KeyStore iasKeyStore;
|
||||
private final String enclaveName;
|
||||
private final byte[] serviceId;
|
||||
private final String mrenclave;
|
||||
private final PushServiceSocket pushServiceSocket;
|
||||
private final int maxTries;
|
||||
|
||||
KeyBackupService(KeyStore iasKeyStore,
|
||||
String enclaveName,
|
||||
byte[] serviceId,
|
||||
String mrenclave,
|
||||
PushServiceSocket pushServiceSocket,
|
||||
int maxTries)
|
||||
{
|
||||
this.iasKeyStore = iasKeyStore;
|
||||
this.enclaveName = enclaveName;
|
||||
this.serviceId = serviceId;
|
||||
this.mrenclave = mrenclave;
|
||||
this.pushServiceSocket = pushServiceSocket;
|
||||
this.maxTries = maxTries;
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this if you don't want to validate that the server has not changed since you last set the pin.
|
||||
*/
|
||||
public PinChangeSession newPinChangeSession()
|
||||
throws IOException
|
||||
{
|
||||
return newSession(pushServiceSocket.getKeyBackupServiceAuthorization().asBasic(), null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this if you want to validate that the server has not changed since you last set the pin.
|
||||
* The supplied token will have to match for the change to be successful.
|
||||
*/
|
||||
public PinChangeSession newPinChangeSession(TokenResponse currentToken)
|
||||
throws IOException
|
||||
{
|
||||
return newSession(pushServiceSocket.getKeyBackupServiceAuthorization().asBasic(), currentToken);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only call before registration, to see how many tries are left.
|
||||
* <p>
|
||||
* Pass the token to {@link #newRegistrationSession(String, TokenResponse)}.
|
||||
*/
|
||||
public TokenResponse getToken(String authAuthorization) throws IOException {
|
||||
return pushServiceSocket.getKeyBackupServiceToken(authAuthorization, enclaveName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the authorization token to be used with other requests.
|
||||
*/
|
||||
public AuthCredentials getAuthorization() throws IOException {
|
||||
return pushServiceSocket.getKeyBackupServiceAuthorization();
|
||||
}
|
||||
|
||||
/**
|
||||
* Use this during registration, good for one try, on subsequent attempts, pass the token from the previous attempt.
|
||||
*
|
||||
* @param tokenResponse Supplying a token response from a failed previous attempt prevents certain attacks.
|
||||
*/
|
||||
public RestoreSession newRegistrationSession(String authAuthorization, TokenResponse tokenResponse)
|
||||
throws IOException
|
||||
{
|
||||
return newSession(authAuthorization, tokenResponse);
|
||||
}
|
||||
|
||||
public String getEnclaveName() {
|
||||
return enclaveName;
|
||||
}
|
||||
|
||||
public String getMrenclave() {
|
||||
return mrenclave;
|
||||
}
|
||||
|
||||
private Session newSession(String authorization, TokenResponse currentToken)
|
||||
throws IOException
|
||||
{
|
||||
TokenResponse token = currentToken != null ? currentToken : pushServiceSocket.getKeyBackupServiceToken(authorization, enclaveName);
|
||||
|
||||
return new Session(authorization, token);
|
||||
}
|
||||
|
||||
private class Session implements RestoreSession, PinChangeSession {
|
||||
|
||||
private final String authorization;
|
||||
private final TokenResponse currentToken;
|
||||
|
||||
Session(String authorization, TokenResponse currentToken) {
|
||||
this.authorization = authorization;
|
||||
this.currentToken = currentToken;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] hashSalt() {
|
||||
return currentToken.getBackupId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public SvrPinData restorePin(PinHash hashedPin)
|
||||
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException, SvrNoDataException, InvalidKeyException
|
||||
{
|
||||
int attempt = 0;
|
||||
SecureRandom random = new SecureRandom();
|
||||
TokenResponse token = currentToken;
|
||||
|
||||
while (true) {
|
||||
|
||||
attempt++;
|
||||
|
||||
try {
|
||||
return restorePin(hashedPin, token);
|
||||
} catch (TokenException tokenException) {
|
||||
|
||||
token = tokenException.getToken();
|
||||
|
||||
if (tokenException instanceof KeyBackupServicePinException) {
|
||||
throw (KeyBackupServicePinException) tokenException;
|
||||
}
|
||||
|
||||
if (tokenException.isCanAutomaticallyRetry() && attempt < 5) {
|
||||
// back off randomly, between 250 and 8000 ms
|
||||
int backoffMs = 250 * (1 << (attempt - 1));
|
||||
|
||||
Util.sleep(backoffMs + random.nextInt(backoffMs));
|
||||
} else {
|
||||
throw new UnauthenticatedResponseException("Token mismatch, expended all automatic retries");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private SvrPinData restorePin(PinHash hashedPin, TokenResponse token)
|
||||
throws UnauthenticatedResponseException, IOException, TokenException, SvrNoDataException, InvalidKeyException
|
||||
{
|
||||
try {
|
||||
final int remainingTries = token.getTries();
|
||||
final RemoteAttestation remoteAttestation = getAndVerifyRemoteAttestation();
|
||||
final KeyBackupRequest request = KeyBackupCipher.createKeyRestoreRequest(hashedPin.accessKey(), token, remoteAttestation, serviceId);
|
||||
final KeyBackupResponse response = pushServiceSocket.putKbsData(authorization, request, remoteAttestation.getCookies(), enclaveName);
|
||||
final RestoreResponse status = KeyBackupCipher.getKeyRestoreResponse(response, remoteAttestation);
|
||||
|
||||
TokenResponse nextToken = status.token != null ? new TokenResponse(token.getBackupId(), status.token.toByteArray(), status.tries)
|
||||
: token;
|
||||
|
||||
Log.i(TAG, "Restore " + status.status);
|
||||
switch (status.status) {
|
||||
case OK:
|
||||
KbsData kbsData = PinHashUtil.decryptSvrDataIVCipherText(hashedPin, status.data_.toByteArray());
|
||||
MasterKey masterKey = kbsData.getMasterKey();
|
||||
return new SvrPinData(masterKey, nextToken);
|
||||
case PIN_MISMATCH:
|
||||
Log.i(TAG, "Restore PIN_MISMATCH");
|
||||
throw new KeyBackupServicePinException(nextToken);
|
||||
case TOKEN_MISMATCH:
|
||||
Log.i(TAG, "Restore TOKEN_MISMATCH");
|
||||
// if the number of tries has not fallen, the pin is correct we're just using an out of date token
|
||||
boolean canRetry = remainingTries == status.tries;
|
||||
Log.i(TAG, String.format(Locale.US, "Token MISMATCH remainingTries: %d, status.getTries(): %d", remainingTries, status.tries));
|
||||
throw new TokenException(nextToken, canRetry);
|
||||
case MISSING:
|
||||
Log.i(TAG, "Restore OK! No data though");
|
||||
throw new SvrNoDataException();
|
||||
case NOT_YET_VALID:
|
||||
throw new UnauthenticatedResponseException("Key is not valid yet, clock mismatch");
|
||||
default:
|
||||
throw new AssertionError("Unexpected case");
|
||||
}
|
||||
} catch (InvalidCiphertextException e) {
|
||||
throw new UnauthenticatedResponseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
private RemoteAttestation getAndVerifyRemoteAttestation() throws UnauthenticatedResponseException, IOException, InvalidKeyException {
|
||||
try {
|
||||
return RemoteAttestationUtil.getAndVerifyRemoteAttestation(pushServiceSocket, PushServiceSocket.ClientSet.KeyBackup, iasKeyStore, enclaveName, mrenclave, authorization);
|
||||
} catch (Quote.InvalidQuoteFormatException | UnauthenticatedQuoteException | InvalidCiphertextException | SignatureException e) {
|
||||
throw new UnauthenticatedResponseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public SvrPinData setPin(PinHash pinHash, MasterKey masterKey) throws IOException, UnauthenticatedResponseException {
|
||||
KbsData newKbsData = PinHashUtil.createNewKbsData(pinHash, masterKey);
|
||||
TokenResponse tokenResponse = putKbsData(newKbsData.getKbsAccessKey(),
|
||||
newKbsData.getCipherText(),
|
||||
enclaveName,
|
||||
currentToken);
|
||||
|
||||
return new SvrPinData(masterKey, tokenResponse);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removePin()
|
||||
throws IOException, UnauthenticatedResponseException
|
||||
{
|
||||
try {
|
||||
RemoteAttestation remoteAttestation = getAndVerifyRemoteAttestation();
|
||||
KeyBackupRequest request = KeyBackupCipher.createKeyDeleteRequest(currentToken, remoteAttestation, serviceId);
|
||||
KeyBackupResponse response = pushServiceSocket.putKbsData(authorization, request, remoteAttestation.getCookies(), enclaveName);
|
||||
|
||||
KeyBackupCipher.getKeyDeleteResponseStatus(response, remoteAttestation);
|
||||
} catch (InvalidCiphertextException | InvalidKeyException e) {
|
||||
throw new UnauthenticatedResponseException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void enableRegistrationLock(MasterKey masterKey) throws IOException {
|
||||
pushServiceSocket.setRegistrationLockV2(masterKey.deriveRegistrationLock());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void disableRegistrationLock() throws IOException {
|
||||
pushServiceSocket.disableRegistrationLockV2();
|
||||
}
|
||||
|
||||
private TokenResponse putKbsData(byte[] kbsAccessKey, byte[] kbsData, String enclaveName, TokenResponse token)
|
||||
throws IOException, UnauthenticatedResponseException
|
||||
{
|
||||
try {
|
||||
RemoteAttestation remoteAttestation = getAndVerifyRemoteAttestation();
|
||||
KeyBackupRequest request = KeyBackupCipher.createKeyBackupRequest(kbsAccessKey, kbsData, token, remoteAttestation, serviceId, maxTries);
|
||||
KeyBackupResponse response = pushServiceSocket.putKbsData(authorization, request, remoteAttestation.getCookies(), enclaveName);
|
||||
BackupResponse backupResponse = KeyBackupCipher.getKeyBackupResponse(response, remoteAttestation);
|
||||
BackupResponse.Status status = backupResponse.status;
|
||||
|
||||
switch (status) {
|
||||
case OK:
|
||||
return backupResponse.token != null ? new TokenResponse(token.getBackupId(), backupResponse.token.toByteArray(), maxTries) : token;
|
||||
case ALREADY_EXISTS:
|
||||
throw new UnauthenticatedResponseException("Already exists");
|
||||
case NOT_YET_VALID:
|
||||
throw new UnauthenticatedResponseException("Key is not valid yet, clock mismatch");
|
||||
default:
|
||||
throw new AssertionError("Unknown response status " + status);
|
||||
}
|
||||
} catch (InvalidCiphertextException | InvalidKeyException e) {
|
||||
throw new UnauthenticatedResponseException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface HashSession {
|
||||
|
||||
byte[] hashSalt();
|
||||
}
|
||||
|
||||
public interface RestoreSession extends HashSession {
|
||||
|
||||
SvrPinData restorePin(PinHash hashedPin)
|
||||
throws UnauthenticatedResponseException, IOException, KeyBackupServicePinException, SvrNoDataException, InvalidKeyException;
|
||||
}
|
||||
|
||||
public interface PinChangeSession extends HashSession {
|
||||
/** Creates a PIN. Does nothing to registration lock. */
|
||||
SvrPinData setPin(PinHash hashedPin, MasterKey masterKey) throws IOException, UnauthenticatedResponseException;
|
||||
|
||||
/** Removes the PIN data from KBS. */
|
||||
void removePin() throws IOException, UnauthenticatedResponseException;
|
||||
|
||||
/** Enables registration lock. This assumes a PIN is set. */
|
||||
void enableRegistrationLock(MasterKey masterKey) throws IOException;
|
||||
|
||||
/** Disables registration lock. The user keeps their PIN. */
|
||||
void disableRegistrationLock() throws IOException;
|
||||
}
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
package org.whispersystems.signalservice.api;
|
||||
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.TokenResponse;
|
||||
|
||||
public final class KeyBackupServicePinException extends TokenException {
|
||||
|
||||
private final int triesRemaining;
|
||||
|
||||
public KeyBackupServicePinException(TokenResponse nextToken) {
|
||||
super(nextToken, false);
|
||||
this.triesRemaining = nextToken.getTries();
|
||||
}
|
||||
|
||||
public int getTriesRemaining() {
|
||||
return triesRemaining;
|
||||
}
|
||||
}
|
|
@ -179,30 +179,10 @@ public class SignalServiceAccountManager {
|
|||
return new SecureValueRecoveryV2(configuration, mrEnclave, pushServiceSocket);
|
||||
}
|
||||
|
||||
/**
|
||||
* V1 PINs are no longer used in favor of V2 PINs stored on KBS.
|
||||
*
|
||||
* You can remove a V1 PIN, but typically this is unnecessary, as setting a V2 PIN via
|
||||
* {@link KeyBackupService.PinChangeSession#enableRegistrationLock(MasterKey)}} will automatically clear the
|
||||
* V1 PIN on the service.
|
||||
*/
|
||||
public void removeRegistrationLockV1() throws IOException {
|
||||
this.pushServiceSocket.removeRegistrationLockV1();
|
||||
}
|
||||
|
||||
public WhoAmIResponse getWhoAmI() throws IOException {
|
||||
return this.pushServiceSocket.getWhoAmI();
|
||||
}
|
||||
|
||||
public KeyBackupService getKeyBackupService(KeyStore iasKeyStore,
|
||||
String enclaveName,
|
||||
byte[] serviceId,
|
||||
String mrenclave,
|
||||
int tries)
|
||||
{
|
||||
return new KeyBackupService(iasKeyStore, enclaveName, serviceId, mrenclave, pushServiceSocket, tries);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register/Unregister a Google Cloud Messaging registration ID.
|
||||
*
|
||||
|
|
|
@ -1,124 +0,0 @@
|
|||
/*
|
||||
* Copyright 2023 Signal Messenger, LLC
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
package org.whispersystems.signalservice.api.svr
|
||||
|
||||
import org.signal.libsignal.svr2.PinHash
|
||||
import org.whispersystems.signalservice.api.KeyBackupService
|
||||
import org.whispersystems.signalservice.api.KeyBackupServicePinException
|
||||
import org.whispersystems.signalservice.api.SvrNoDataException
|
||||
import org.whispersystems.signalservice.api.SvrPinData
|
||||
import org.whispersystems.signalservice.api.kbs.MasterKey
|
||||
import org.whispersystems.signalservice.api.kbs.PinHashUtil
|
||||
import org.whispersystems.signalservice.api.push.exceptions.NonSuccessfulResponseCodeException
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.BackupResponse
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.DeleteResponse
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.PinChangeSession
|
||||
import org.whispersystems.signalservice.api.svr.SecureValueRecovery.RestoreResponse
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedResponseException
|
||||
import org.whispersystems.signalservice.internal.push.AuthCredentials
|
||||
import java.io.IOException
|
||||
import kotlin.jvm.Throws
|
||||
|
||||
/**
|
||||
* An implementation of the [SecureValueRecovery] interface backed by the [KeyBackupService].
|
||||
*/
|
||||
class SecureValueRecoveryV1(private val kbs: KeyBackupService) : SecureValueRecovery {
|
||||
|
||||
companion object {
|
||||
const val TAG = "SVR1"
|
||||
}
|
||||
|
||||
override fun setPin(userPin: String, masterKey: MasterKey): PinChangeSession {
|
||||
return Svr1PinChangeSession(userPin, masterKey)
|
||||
}
|
||||
|
||||
override fun resumePinChangeSession(userPin: String, masterKey: MasterKey, serializedChangeSession: String): PinChangeSession {
|
||||
return setPin(userPin, masterKey)
|
||||
}
|
||||
|
||||
override fun restoreDataPreRegistration(authorization: AuthCredentials, userPin: String): RestoreResponse {
|
||||
return restoreData({ authorization }, userPin)
|
||||
}
|
||||
|
||||
override fun restoreDataPostRegistration(userPin: String): RestoreResponse {
|
||||
return restoreData({ kbs.authorization }, userPin)
|
||||
}
|
||||
|
||||
override fun deleteData(): DeleteResponse {
|
||||
return try {
|
||||
kbs.newPinChangeSession().removePin()
|
||||
DeleteResponse.Success
|
||||
} catch (e: UnauthenticatedResponseException) {
|
||||
DeleteResponse.ApplicationError(e)
|
||||
} catch (e: NonSuccessfulResponseCodeException) {
|
||||
when (e.code) {
|
||||
404 -> DeleteResponse.EnclaveNotFound
|
||||
508 -> DeleteResponse.ServerRejected
|
||||
else -> DeleteResponse.NetworkError(e)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
DeleteResponse.NetworkError(e)
|
||||
}
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun authorization(): AuthCredentials {
|
||||
return kbs.authorization
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "SVR1::${kbs.enclaveName}::${kbs.mrenclave}"
|
||||
}
|
||||
|
||||
private fun restoreData(fetchAuthorization: () -> AuthCredentials, userPin: String): RestoreResponse {
|
||||
return try {
|
||||
val authorization: AuthCredentials = fetchAuthorization()
|
||||
val session = kbs.newRegistrationSession(authorization.asBasic(), null)
|
||||
val pinHash: PinHash = PinHashUtil.hashPin(userPin, session.hashSalt())
|
||||
|
||||
val data: SvrPinData = session.restorePin(pinHash)
|
||||
RestoreResponse.Success(data.masterKey, authorization)
|
||||
} catch (e: SvrNoDataException) {
|
||||
RestoreResponse.Missing
|
||||
} catch (e: KeyBackupServicePinException) {
|
||||
RestoreResponse.PinMismatch(e.triesRemaining)
|
||||
} catch (e: IOException) {
|
||||
RestoreResponse.NetworkError(e)
|
||||
} catch (e: Exception) {
|
||||
RestoreResponse.ApplicationError(e)
|
||||
}
|
||||
}
|
||||
|
||||
inner class Svr1PinChangeSession(
|
||||
private val userPin: String,
|
||||
private val masterKey: MasterKey
|
||||
) : PinChangeSession {
|
||||
override fun execute(): BackupResponse {
|
||||
return try {
|
||||
val session = kbs.newPinChangeSession()
|
||||
val pinHash: PinHash = PinHashUtil.hashPin(userPin, session.hashSalt())
|
||||
|
||||
val data: SvrPinData = session.setPin(pinHash, masterKey)
|
||||
BackupResponse.Success(data.masterKey, kbs.authorization)
|
||||
} catch (e: UnauthenticatedResponseException) {
|
||||
BackupResponse.ApplicationError(e)
|
||||
} catch (e: NonSuccessfulResponseCodeException) {
|
||||
when (e.code) {
|
||||
404 -> BackupResponse.EnclaveNotFound
|
||||
508 -> BackupResponse.ServerRejected
|
||||
else -> BackupResponse.NetworkError(e)
|
||||
}
|
||||
} catch (e: IOException) {
|
||||
BackupResponse.NetworkError(e)
|
||||
}
|
||||
}
|
||||
|
||||
/** No real need to serialize */
|
||||
override fun serialize(): String {
|
||||
return ""
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,7 +10,6 @@ import java.util.Optional
|
|||
class SignalServiceConfiguration(
|
||||
val signalServiceUrls: Array<SignalServiceUrl>,
|
||||
val signalCdnUrlMap: Map<Int, Array<SignalCdnUrl>>,
|
||||
val signalKeyBackupServiceUrls: Array<SignalKeyBackupServiceUrl>,
|
||||
val signalStorageUrls: Array<SignalStorageUrl>,
|
||||
val signalCdsiUrls: Array<SignalCdsiUrl>,
|
||||
val signalSvr2Urls: Array<SignalSvr2Url>,
|
||||
|
|
|
@ -316,7 +316,6 @@ public class PushServiceSocket {
|
|||
|
||||
private final ServiceConnectionHolder[] serviceClients;
|
||||
private final Map<Integer, ConnectionHolder[]> cdnClientsMap;
|
||||
private final ConnectionHolder[] keyBackupServiceClients;
|
||||
private final ConnectionHolder[] storageClients;
|
||||
|
||||
private final CredentialsProvider credentialsProvider;
|
||||
|
@ -336,7 +335,6 @@ public class PushServiceSocket {
|
|||
this.automaticNetworkRetry = automaticNetworkRetry;
|
||||
this.serviceClients = createServiceConnectionHolders(configuration.getSignalServiceUrls(), configuration.getNetworkInterceptors(), configuration.getDns(), configuration.getSignalProxy());
|
||||
this.cdnClientsMap = createCdnClientsMap(configuration.getSignalCdnUrlMap(), configuration.getNetworkInterceptors(), configuration.getDns(), configuration.getSignalProxy());
|
||||
this.keyBackupServiceClients = createConnectionHolders(configuration.getSignalKeyBackupServiceUrls(), configuration.getNetworkInterceptors(), configuration.getDns(), configuration.getSignalProxy());
|
||||
this.storageClients = createConnectionHolders(configuration.getSignalStorageUrls(), configuration.getNetworkInterceptors(), configuration.getDns(), configuration.getSignalProxy());
|
||||
this.random = new SecureRandom();
|
||||
this.clientZkProfileOperations = clientZkProfileOperations;
|
||||
|
@ -1281,34 +1279,10 @@ public class PushServiceSocket {
|
|||
return getAuthCredentials(authPath).asBasic();
|
||||
}
|
||||
|
||||
public String getContactDiscoveryAuthorization() throws IOException {
|
||||
return getCredentials(DIRECTORY_AUTH_PATH);
|
||||
}
|
||||
|
||||
public AuthCredentials getKeyBackupServiceAuthorization() throws IOException {
|
||||
return getAuthCredentials(KBS_AUTH_PATH);
|
||||
}
|
||||
|
||||
public AuthCredentials getPaymentsAuthorization() throws IOException {
|
||||
return getAuthCredentials(PAYMENTS_AUTH_PATH);
|
||||
}
|
||||
|
||||
public TokenResponse getKeyBackupServiceToken(String authorizationToken, String enclaveName)
|
||||
throws IOException
|
||||
{
|
||||
try (Response response = makeRequest(ClientSet.KeyBackup, authorizationToken, null, "/v1/token/" + enclaveName, "GET", null)) {
|
||||
return readBodyJson(response, TokenResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
public KeyBackupResponse putKbsData(String authorizationToken, KeyBackupRequest request, List<String> cookies, String mrenclave)
|
||||
throws IOException
|
||||
{
|
||||
try (Response response = makeRequest(ClientSet.KeyBackup, authorizationToken, cookies, "/v1/backup/" + mrenclave, "PUT", JsonUtil.toJson(request))) {
|
||||
return readBodyJson(response, KeyBackupResponse.class);
|
||||
}
|
||||
}
|
||||
|
||||
public TurnServerInfo getTurnServerInfo() throws IOException {
|
||||
String response = makeServiceRequest(TURN_SERVER_INFO, "GET", null);
|
||||
return JsonUtil.fromJson(response, TurnServerInfo.class);
|
||||
|
@ -2113,82 +2087,6 @@ public class PushServiceSocket {
|
|||
return request.build();
|
||||
}
|
||||
|
||||
|
||||
private ConnectionHolder[] clientsFor(ClientSet clientSet) {
|
||||
switch (clientSet) {
|
||||
case KeyBackup:
|
||||
return keyBackupServiceClients;
|
||||
default:
|
||||
throw new AssertionError("Unknown attestation purpose");
|
||||
}
|
||||
}
|
||||
|
||||
Response makeRequest(ClientSet clientSet, String authorization, List<String> cookies, String path, String method, String body)
|
||||
throws PushNetworkException, NonSuccessfulResponseCodeException
|
||||
{
|
||||
ConnectionHolder connectionHolder = getRandom(clientsFor(clientSet), random);
|
||||
|
||||
OkHttpClient okHttpClient = connectionHolder.getClient()
|
||||
.newBuilder()
|
||||
.connectTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.readTimeout(soTimeoutMillis, TimeUnit.MILLISECONDS)
|
||||
.build();
|
||||
|
||||
Request.Builder request = new Request.Builder().url(connectionHolder.getUrl() + path);
|
||||
|
||||
if (body != null) {
|
||||
request.method(method, RequestBody.create(MediaType.parse("application/json"), body));
|
||||
} else {
|
||||
request.method(method, null);
|
||||
}
|
||||
|
||||
if (connectionHolder.getHostHeader().isPresent()) {
|
||||
request.addHeader("Host", connectionHolder.getHostHeader().get());
|
||||
}
|
||||
|
||||
if (authorization != null) {
|
||||
request.addHeader("Authorization", authorization);
|
||||
}
|
||||
|
||||
if (cookies != null && !cookies.isEmpty()) {
|
||||
request.addHeader("Cookie", Util.join(cookies, "; "));
|
||||
}
|
||||
|
||||
Call call = okHttpClient.newCall(request.build());
|
||||
|
||||
synchronized (connections) {
|
||||
connections.add(call);
|
||||
}
|
||||
|
||||
Response response;
|
||||
|
||||
try {
|
||||
response = call.execute();
|
||||
|
||||
if (response.isSuccessful()) {
|
||||
return response;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new PushNetworkException(e);
|
||||
} finally {
|
||||
synchronized (connections) {
|
||||
connections.remove(call);
|
||||
}
|
||||
}
|
||||
|
||||
switch (response.code()) {
|
||||
case 401:
|
||||
case 403:
|
||||
throw new AuthorizationFailedException(response.code(), "Authorization failed!");
|
||||
case 409:
|
||||
throw new RemoteAttestationResponseExpiredException("Remote attestation response expired");
|
||||
case 429:
|
||||
throw new RateLimitException(response.code(), "Rate limit exceeded: " + response.code());
|
||||
}
|
||||
|
||||
throw new NonSuccessfulResponseCodeException(response.code(), "Response: " + response);
|
||||
}
|
||||
|
||||
private Response makeStorageRequest(String authorization, String path, String method, RequestBody body, ResponseCodeHandler responseCodeHandler)
|
||||
throws PushNetworkException, NonSuccessfulResponseCodeException
|
||||
{
|
||||
|
|
|
@ -1,135 +0,0 @@
|
|||
package org.whispersystems.signalservice.internal.push;
|
||||
|
||||
import org.signal.libsignal.protocol.InvalidKeyException;
|
||||
import org.signal.libsignal.protocol.ecc.Curve;
|
||||
import org.signal.libsignal.protocol.ecc.ECKeyPair;
|
||||
import org.whispersystems.signalservice.api.crypto.InvalidCiphertextException;
|
||||
import org.whispersystems.signalservice.api.push.exceptions.MalformedResponseException;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.Quote;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestation;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestationCipher;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.RemoteAttestationKeys;
|
||||
import org.whispersystems.signalservice.internal.contacts.crypto.UnauthenticatedQuoteException;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.MultiRemoteAttestationResponse;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationRequest;
|
||||
import org.whispersystems.signalservice.internal.contacts.entities.RemoteAttestationResponse;
|
||||
import org.whispersystems.signalservice.internal.util.JsonUtil;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.security.KeyStore;
|
||||
import java.security.SignatureException;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
public final class RemoteAttestationUtil {
|
||||
|
||||
private RemoteAttestationUtil() {
|
||||
}
|
||||
|
||||
public static RemoteAttestation getAndVerifyRemoteAttestation(PushServiceSocket socket,
|
||||
PushServiceSocket.ClientSet clientSet,
|
||||
KeyStore iasKeyStore,
|
||||
String enclaveName,
|
||||
String mrenclave,
|
||||
String authorization)
|
||||
throws IOException, Quote.InvalidQuoteFormatException, InvalidCiphertextException, UnauthenticatedQuoteException, SignatureException, InvalidKeyException
|
||||
{
|
||||
ECKeyPair keyPair = buildKeyPair();
|
||||
ResponsePair result = makeAttestationRequest(socket, clientSet, authorization, enclaveName, keyPair);
|
||||
RemoteAttestationResponse response = JsonUtil.fromJson(result.body, RemoteAttestationResponse.class);
|
||||
|
||||
return validateAndBuildRemoteAttestation(response, result.cookies, iasKeyStore, keyPair, mrenclave);
|
||||
}
|
||||
|
||||
public static Map<String, RemoteAttestation> getAndVerifyMultiRemoteAttestation(PushServiceSocket socket,
|
||||
PushServiceSocket.ClientSet clientSet,
|
||||
KeyStore iasKeyStore,
|
||||
String enclaveName,
|
||||
String mrenclave,
|
||||
String authorization)
|
||||
throws IOException, Quote.InvalidQuoteFormatException, InvalidCiphertextException, UnauthenticatedQuoteException, SignatureException, InvalidKeyException
|
||||
{
|
||||
ECKeyPair keyPair = buildKeyPair();
|
||||
ResponsePair result = makeAttestationRequest(socket, clientSet, authorization, enclaveName, keyPair);
|
||||
MultiRemoteAttestationResponse response = JsonUtil.fromJson(result.body, MultiRemoteAttestationResponse.class);
|
||||
Map<String, RemoteAttestation> attestations = new HashMap<>();
|
||||
|
||||
if (response.getAttestations().isEmpty() || response.getAttestations().size() > 3) {
|
||||
throw new MalformedResponseException("Incorrect number of attestations: " + response.getAttestations().size());
|
||||
}
|
||||
|
||||
for (Map.Entry<String, RemoteAttestationResponse> entry : response.getAttestations().entrySet()) {
|
||||
attestations.put(entry.getKey(),
|
||||
validateAndBuildRemoteAttestation(entry.getValue(),
|
||||
result.cookies,
|
||||
iasKeyStore,
|
||||
keyPair,
|
||||
mrenclave));
|
||||
}
|
||||
|
||||
return attestations;
|
||||
}
|
||||
|
||||
private static ECKeyPair buildKeyPair() {
|
||||
return Curve.generateKeyPair();
|
||||
}
|
||||
|
||||
private static ResponsePair makeAttestationRequest(PushServiceSocket socket,
|
||||
PushServiceSocket.ClientSet clientSet,
|
||||
String authorization,
|
||||
String enclaveName,
|
||||
ECKeyPair keyPair)
|
||||
throws IOException
|
||||
{
|
||||
RemoteAttestationRequest attestationRequest = new RemoteAttestationRequest(keyPair.getPublicKey().getPublicKeyBytes());
|
||||
|
||||
try (Response response = socket.makeRequest(clientSet, authorization, Collections.emptyList(), "/v1/attestation/" +enclaveName, "PUT", JsonUtil.toJson(attestationRequest))) {
|
||||
return new ResponsePair(PushServiceSocket.readBodyString(response), parseCookies(response));
|
||||
}
|
||||
}
|
||||
|
||||
private static List<String> parseCookies(Response response) {
|
||||
List<String> rawCookies = response.headers("Set-Cookie");
|
||||
List<String> cookies = new LinkedList<>();
|
||||
|
||||
for (String cookie : rawCookies) {
|
||||
cookies.add(cookie.split(";")[0]);
|
||||
}
|
||||
|
||||
return cookies;
|
||||
}
|
||||
|
||||
private static RemoteAttestation validateAndBuildRemoteAttestation(RemoteAttestationResponse response,
|
||||
List<String> cookies,
|
||||
KeyStore iasKeyStore,
|
||||
ECKeyPair keyPair,
|
||||
String mrenclave)
|
||||
throws Quote.InvalidQuoteFormatException, InvalidCiphertextException, UnauthenticatedQuoteException, SignatureException, InvalidKeyException
|
||||
{
|
||||
RemoteAttestationKeys keys = new RemoteAttestationKeys(keyPair, response.getServerEphemeralPublic(), response.getServerStaticPublic());
|
||||
Quote quote = new Quote(response.getQuote());
|
||||
byte[] requestId = RemoteAttestationCipher.getRequestId(keys, response);
|
||||
|
||||
RemoteAttestationCipher.verifyServerQuote(quote, response.getServerStaticPublic(), mrenclave);
|
||||
|
||||
RemoteAttestationCipher.verifyIasSignature(iasKeyStore, response.getCertificates(), response.getSignatureBody(), response.getSignature(), quote);
|
||||
|
||||
return new RemoteAttestation(requestId, keys, cookies);
|
||||
}
|
||||
|
||||
private static class ResponsePair {
|
||||
final String body;
|
||||
final List<String> cookies;
|
||||
|
||||
private ResponsePair(String body, List<String> cookies) {
|
||||
this.body = body;
|
||||
this.cookies = cookies;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue