From 6e17e47e052411895b525d49ae642155d2f6539e Mon Sep 17 00:00:00 2001 From: benk10 Date: Wed, 12 Aug 2026 14:07:58 -0500 Subject: [PATCH 1/3] fix: fail closed without vss auth Co-authored-by: Cursor --- .../to/bitkit/data/backup/VssBackupClient.kt | 28 ++++++++----------- .../bitkit/data/backup/VssBackupClientLdk.kt | 4 +++ .../to/bitkit/services/LightningService.kt | 8 +++--- app/src/main/java/to/bitkit/utils/Errors.kt | 1 + changelog.d/next/fail-closed.security.md | 1 + 5 files changed, 22 insertions(+), 20 deletions(-) create mode 100644 changelog.d/next/fail-closed.security.md diff --git a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt index ba3eeb9a4a..a72fa5cf82 100644 --- a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt +++ b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt @@ -5,7 +5,6 @@ import com.synonym.vssclient.VssItem import com.synonym.vssclient.vssDelete import com.synonym.vssclient.vssGet import com.synonym.vssclient.vssListKeys -import com.synonym.vssclient.vssNewClient import com.synonym.vssclient.vssNewClientWithLnurlAuth import com.synonym.vssclient.vssStore import kotlinx.coroutines.CompletableDeferred @@ -19,6 +18,7 @@ import to.bitkit.data.keychain.Keychain import to.bitkit.di.IoDispatcher import to.bitkit.env.Env import to.bitkit.utils.Logger +import to.bitkit.utils.ServiceError import javax.inject.Inject import javax.inject.Singleton import kotlin.time.Duration.Companion.seconds @@ -51,22 +51,18 @@ class VssBackupClient @Inject constructor( val vssStoreId = vssStoreIdProvider.getVssStoreId(walletIndex) Logger.verbose("Building VSS client with vssUrl: '$vssUrl'", context = TAG) Logger.verbose("Building VSS client with lnurlAuthServerUrl: '$lnurlAuthServerUrl'", context = TAG) - if (lnurlAuthServerUrl.isNotEmpty()) { - val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) - - vssNewClientWithLnurlAuth( - baseUrl = vssUrl, - storeId = vssStoreId, - mnemonic = mnemonic, - passphrase = passphrase, - lnurlAuthServerUrl = lnurlAuthServerUrl, - ) - } else { - vssNewClient( - baseUrl = vssUrl, - storeId = vssStoreId, - ) + if (lnurlAuthServerUrl.isEmpty()) { + throw ServiceError.VssAuthRequired() } + val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) + + vssNewClientWithLnurlAuth( + baseUrl = vssUrl, + storeId = vssStoreId, + mnemonic = mnemonic, + passphrase = passphrase, + lnurlAuthServerUrl = lnurlAuthServerUrl, + ) isSetup.complete(Unit) Logger.info("VSS client setup with server: '$vssUrl'", context = TAG) } diff --git a/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt b/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt index 2eec56a291..8ab39eb286 100644 --- a/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt +++ b/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt @@ -17,6 +17,7 @@ import to.bitkit.data.keychain.Keychain import to.bitkit.di.IoDispatcher import to.bitkit.env.Env import to.bitkit.utils.Logger +import to.bitkit.utils.ServiceError import javax.inject.Inject import javax.inject.Singleton import kotlin.time.Duration.Companion.seconds @@ -52,6 +53,9 @@ class VssBackupClientLdk @Inject constructor( ?: throw MnemonicNotAvailableException() withTimeout(30.seconds) { + if (Env.lnurlAuthServerUrl.isEmpty()) { + throw ServiceError.VssAuthRequired() + } val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) vssNewLdkClientWithLnurlAuth( baseUrl = Env.vssServerUrl, diff --git a/app/src/main/java/to/bitkit/services/LightningService.kt b/app/src/main/java/to/bitkit/services/LightningService.kt index 03fe763e9b..8304f2e684 100644 --- a/app/src/main/java/to/bitkit/services/LightningService.kt +++ b/app/src/main/java/to/bitkit/services/LightningService.kt @@ -286,11 +286,11 @@ class LightningService @Inject constructor( context = TAG, ) - if (lnurlAuthServerUrl.isNotEmpty()) { - builder.buildWithVssStore(vssUrl, vssStoreId, lnurlAuthServerUrl, fixedHeaders) - } else { - builder.buildWithVssStoreAndFixedHeaders(vssUrl, vssStoreId, fixedHeaders) + if (lnurlAuthServerUrl.isEmpty()) { + throw ServiceError.VssAuthRequired() } + + builder.buildWithVssStore(vssUrl, vssStoreId, lnurlAuthServerUrl, fixedHeaders) } catch (e: BuildException) { throw LdkError(e) } finally { diff --git a/app/src/main/java/to/bitkit/utils/Errors.kt b/app/src/main/java/to/bitkit/utils/Errors.kt index b42c09099c..f8132a54aa 100644 --- a/app/src/main/java/to/bitkit/utils/Errors.kt +++ b/app/src/main/java/to/bitkit/utils/Errors.kt @@ -16,6 +16,7 @@ sealed class ServiceError(message: String) : AppError(message) { class NodeNotSetup : ServiceError("Node is not setup") class NodeNotStarted : ServiceError("Node is not started") class MnemonicNotFound : ServiceError("Mnemonic not found") + class VssAuthRequired : ServiceError("VSS requires LNURL-auth") class NodeStillRunning : ServiceError("Node is still running") class NodeReleaseTimeout : ServiceError("Previous node release did not finish in time") class InvalidNodeSigningMessage : ServiceError("Invalid node signing message") diff --git a/changelog.d/next/fail-closed.security.md b/changelog.d/next/fail-closed.security.md new file mode 100644 index 0000000000..b4a989b097 --- /dev/null +++ b/changelog.d/next/fail-closed.security.md @@ -0,0 +1 @@ +Wallet backups no longer fall back to unauthenticated VSS when LNURL-auth is missing. From b3981997345a7c3762f3867da52af126a03b5ffb Mon Sep 17 00:00:00 2001 From: benk10 Date: Wed, 12 Aug 2026 14:09:50 -0500 Subject: [PATCH 2/3] chore: rename changelog fragment Co-authored-by: Cursor --- changelog.d/next/{fail-closed.security.md => 1156.security.md} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename changelog.d/next/{fail-closed.security.md => 1156.security.md} (100%) diff --git a/changelog.d/next/fail-closed.security.md b/changelog.d/next/1156.security.md similarity index 100% rename from changelog.d/next/fail-closed.security.md rename to changelog.d/next/1156.security.md From 378bcae37597da3e1b2c2cf653cf0fde921d0c4c Mon Sep 17 00:00:00 2001 From: benk10 Date: Wed, 12 Aug 2026 14:29:17 -0500 Subject: [PATCH 3/3] fix: treat blank vss auth urls as missing Co-authored-by: Cursor --- app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt | 2 +- app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt | 2 +- app/src/main/java/to/bitkit/services/LightningService.kt | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt index a72fa5cf82..d404555329 100644 --- a/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt +++ b/app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt @@ -51,7 +51,7 @@ class VssBackupClient @Inject constructor( val vssStoreId = vssStoreIdProvider.getVssStoreId(walletIndex) Logger.verbose("Building VSS client with vssUrl: '$vssUrl'", context = TAG) Logger.verbose("Building VSS client with lnurlAuthServerUrl: '$lnurlAuthServerUrl'", context = TAG) - if (lnurlAuthServerUrl.isEmpty()) { + if (lnurlAuthServerUrl.isBlank()) { throw ServiceError.VssAuthRequired() } val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) diff --git a/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt b/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt index 8ab39eb286..061723bea4 100644 --- a/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt +++ b/app/src/main/java/to/bitkit/data/backup/VssBackupClientLdk.kt @@ -53,7 +53,7 @@ class VssBackupClientLdk @Inject constructor( ?: throw MnemonicNotAvailableException() withTimeout(30.seconds) { - if (Env.lnurlAuthServerUrl.isEmpty()) { + if (Env.lnurlAuthServerUrl.isBlank()) { throw ServiceError.VssAuthRequired() } val passphrase = keychain.loadString(Keychain.Key.BIP39_PASSPHRASE.name) diff --git a/app/src/main/java/to/bitkit/services/LightningService.kt b/app/src/main/java/to/bitkit/services/LightningService.kt index 8304f2e684..bf6ca4222b 100644 --- a/app/src/main/java/to/bitkit/services/LightningService.kt +++ b/app/src/main/java/to/bitkit/services/LightningService.kt @@ -286,7 +286,7 @@ class LightningService @Inject constructor( context = TAG, ) - if (lnurlAuthServerUrl.isEmpty()) { + if (lnurlAuthServerUrl.isBlank()) { throw ServiceError.VssAuthRequired() }