From 86d6686aee4ffd37b80d895482cc227db0c28705 Mon Sep 17 00:00:00 2001 From: Kayos Date: Sat, 28 Mar 2026 17:18:05 -0700 Subject: [PATCH] feat(matrix): add SecretStorage API and implementation Adds SecretStorage interface and RustSecretStorage implementation for accessing Matrix SSSS (Secure Secret Storage and Sharing). This enables storing and retrieving encrypted secrets using the user's recovery key. Also fixes SDK compatibility issues: - Remove deprecated Sentry configuration from TracingService - Make analytics SDK enableSentryLogging a no-op Requires updated Rust SDK with SecretStoreWrapper FFI. --- .../libraries/matrix/api/MatrixClient.kt | 2 + .../matrix/api/secretstorage/SecretStorage.kt | 55 +++++++++++++++++++ .../libraries/matrix/impl/RustMatrixClient.kt | 3 + .../impl/analytics/RustAnalyticsSdkManager.kt | 4 +- .../impl/secretstorage/RustSecretStorage.kt | 49 +++++++++++++++++ .../matrix/impl/tracing/RustTracingService.kt | 26 ++++----- 6 files changed, 122 insertions(+), 17 deletions(-) create mode 100644 libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/secretstorage/SecretStorage.kt create mode 100644 libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/secretstorage/RustSecretStorage.kt diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt index 773dbaaa07..f5c235da68 100644 --- a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/MatrixClient.kt @@ -20,6 +20,7 @@ import io.element.android.libraries.matrix.api.core.SessionId import io.element.android.libraries.matrix.api.core.UserId import io.element.android.libraries.matrix.api.createroom.CreateRoomParameters import io.element.android.libraries.matrix.api.encryption.EncryptionService +import io.element.android.libraries.matrix.api.secretstorage.SecretStorage import io.element.android.libraries.matrix.api.linknewdevice.LinkDesktopHandler import io.element.android.libraries.matrix.api.linknewdevice.LinkMobileHandler import io.element.android.libraries.matrix.api.media.MatrixMediaLoader @@ -61,6 +62,7 @@ interface MatrixClient { val notificationService: NotificationService val notificationSettingsService: NotificationSettingsService val encryptionService: EncryptionService + val secretStorage: SecretStorage val roomDirectoryService: RoomDirectoryService val mediaPreviewService: MediaPreviewService val matrixMediaLoader: MatrixMediaLoader diff --git a/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/secretstorage/SecretStorage.kt b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/secretstorage/SecretStorage.kt new file mode 100644 index 0000000000..343f592a5d --- /dev/null +++ b/libraries/matrix/api/src/main/kotlin/io/element/android/libraries/matrix/api/secretstorage/SecretStorage.kt @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2026 Sulkta Coop. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package io.element.android.libraries.matrix.api.secretstorage + +/** + * Interface for accessing Matrix SSSS (Secure Secret Storage and Sharing). + * + * This allows storing and retrieving encrypted secrets in the user's + * Matrix account data, using their recovery key for encryption. + */ +interface SecretStorage { + /** + * Open the secret store with a recovery key. + * + * @param recoveryKey The Matrix recovery key (base58 encoded, 48 characters) + * or passphrase that was used to set up SSSS + * @return SecretStore instance if key is valid, null if invalid or SSSS not set up + */ + suspend fun openSecretStore(recoveryKey: String): SecretStore? +} + +/** + * An opened secret store that can read and write secrets. + * + * Secrets are encrypted with the recovery key and stored in the user's + * account data on the homeserver. + */ +interface SecretStore { + /** + * Store a secret encrypted with SSSS. + * + * @param secretName The secret identifier (e.g., "com.sulkta.cardano.wallet_seed") + * @param secret The secret value to store + */ + suspend fun putSecret(secretName: String, secret: String): Result + + /** + * Retrieve a secret from SSSS. + * + * @param secretName The secret identifier + * @return The decrypted secret, or null if not found + */ + suspend fun getSecret(secretName: String): Result + + /** + * Export the recovery key as a base58-encoded string. + * + * This is useful for displaying the key to the user for verification. + */ + fun exportRecoveryKey(): String +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt index 1c87e73ba2..945841f148 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/RustMatrixClient.kt @@ -49,6 +49,7 @@ import io.element.android.libraries.matrix.api.sync.SyncState import io.element.android.libraries.matrix.api.user.MatrixSearchUserResults import io.element.android.libraries.matrix.api.user.MatrixUser import io.element.android.libraries.matrix.impl.encryption.RustEncryptionService +import io.element.android.libraries.matrix.impl.secretstorage.RustSecretStorage import io.element.android.libraries.matrix.impl.exception.mapClientException import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkDesktopHandler import io.element.android.libraries.matrix.impl.linknewdevice.RustLinkMobileHandler @@ -178,6 +179,8 @@ class RustMatrixClient( dispatchers = dispatchers, ) + override val secretStorage = RustSecretStorage(innerClient, dispatchers) + override val roomDirectoryService = RustRoomDirectoryService( client = innerClient, sessionDispatcher = sessionDispatcher, diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/RustAnalyticsSdkManager.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/RustAnalyticsSdkManager.kt index a74acab683..be91571efd 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/RustAnalyticsSdkManager.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/analytics/RustAnalyticsSdkManager.kt @@ -11,12 +11,12 @@ import dev.zacsweers.metro.AppScope import dev.zacsweers.metro.ContributesBinding import io.element.android.services.analytics.api.AnalyticsSdkManager import io.element.android.services.analytics.api.AnalyticsSdkSpan -import org.matrix.rustcomponents.sdk.enableSentryLogging @ContributesBinding(AppScope::class) class RustAnalyticsSdkManager : AnalyticsSdkManager { override fun enableSdkAnalytics(enabled: Boolean) { - enableSentryLogging(enabled) + // Sentry logging was removed from the Rust SDK + // This is now a no-op } override fun startSpan(name: String, parentTraceId: String?): AnalyticsSdkSpan { diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/secretstorage/RustSecretStorage.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/secretstorage/RustSecretStorage.kt new file mode 100644 index 0000000000..4f205bfb26 --- /dev/null +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/secretstorage/RustSecretStorage.kt @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2026 Sulkta Coop. + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package io.element.android.libraries.matrix.impl.secretstorage + +import io.element.android.libraries.core.coroutine.CoroutineDispatchers +import io.element.android.libraries.matrix.api.secretstorage.SecretStorage +import io.element.android.libraries.matrix.api.secretstorage.SecretStore +import kotlinx.coroutines.withContext +import org.matrix.rustcomponents.sdk.Client +import org.matrix.rustcomponents.sdk.SecretStoreWrapper + +/** + * Implementation of [SecretStorage] backed by the Rust SDK. + */ +class RustSecretStorage( + private val client: Client, + private val dispatchers: CoroutineDispatchers, +) : SecretStorage { + + override suspend fun openSecretStore(recoveryKey: String): SecretStore? = + withContext(dispatchers.io) { + client.openSecretStore(recoveryKey)?.let { RustSecretStore(it, dispatchers) } + } +} + +/** + * Implementation of [SecretStore] backed by the Rust SDK SecretStoreWrapper. + */ +class RustSecretStore( + private val inner: SecretStoreWrapper, + private val dispatchers: CoroutineDispatchers, +) : SecretStore { + + override suspend fun putSecret(secretName: String, secret: String): Result = + withContext(dispatchers.io) { + runCatching { inner.putSecret(secretName, secret) } + } + + override suspend fun getSecret(secretName: String): Result = + withContext(dispatchers.io) { + runCatching { inner.getSecret(secretName) } + } + + override fun exportRecoveryKey(): String = inner.exportRecoveryKey() +} diff --git a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt index cad3c83443..d1c2b81612 100644 --- a/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt +++ b/libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/tracing/RustTracingService.kt @@ -17,7 +17,6 @@ import io.element.android.libraries.matrix.api.tracing.LogLevel import io.element.android.libraries.matrix.api.tracing.TracingConfiguration import io.element.android.libraries.matrix.api.tracing.TracingService import io.element.android.libraries.matrix.api.tracing.WriteToFilesConfiguration -import org.matrix.rustcomponents.sdk.SentryConfig import org.matrix.rustcomponents.sdk.TracingFileConfiguration import org.matrix.rustcomponents.sdk.reloadTracingFileWriter import timber.log.Timber @@ -60,17 +59,14 @@ private fun WriteToFilesConfiguration.toTracingFileConfiguration(): TracingFileC } } -fun TracingConfiguration.map(buildMeta: BuildMeta): org.matrix.rustcomponents.sdk.TracingConfiguration = org.matrix.rustcomponents.sdk.TracingConfiguration( - writeToStdoutOrSystem = writesToLogcat, - logLevel = logLevel.toRustLogLevel(), - extraTargets = extraTargets, - traceLogPacks = traceLogPacks.map(), - writeToFiles = writesToFilesConfiguration.toTracingFileConfiguration(), - sentryConfig = sdkSentryDsn?.let { - SentryConfig( - dsn = it, - appVersion = buildMeta.versionName, - appPlatform = "Android", - ) - } -) +@Suppress("UNUSED_PARAMETER") +fun TracingConfiguration.map(buildMeta: BuildMeta): org.matrix.rustcomponents.sdk.TracingConfiguration { + // Note: sdkSentryDsn is no longer supported by the Rust SDK + return org.matrix.rustcomponents.sdk.TracingConfiguration( + writeToStdoutOrSystem = writesToLogcat, + logLevel = logLevel.toRustLogLevel(), + extraTargets = extraTargets, + traceLogPacks = traceLogPacks.map(), + writeToFiles = writesToFilesConfiguration.toTracingFileConfiguration(), + ) +}