Skip to content

Commit bb359f9

Browse files
committed
refactor: replace peek+wipe with use
1 parent 5df97aa commit bb359f9

File tree

10 files changed

+242
-128
lines changed

10 files changed

+242
-128
lines changed

app/src/main/java/to/bitkit/data/backup/VssBackupClient.kt

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ import kotlinx.coroutines.withContext
1717
import kotlinx.coroutines.withTimeout
1818
import to.bitkit.data.keychain.Keychain
1919
import to.bitkit.di.IoDispatcher
20-
import to.bitkit.domain.models.useAsString
2120
import to.bitkit.env.Env
2221
import to.bitkit.utils.Logger
2322
import javax.inject.Inject
@@ -55,16 +54,13 @@ class VssBackupClient @Inject constructor(
5554
if (lnurlAuthServerUrl.isNotEmpty()) {
5655
val passphraseSecret = keychain.loadSecret(Keychain.Key.BIP39_PASSPHRASE.name)
5756

58-
mnemonicSecret.useAsString { mnemonic ->
59-
vssNewClientWithLnurlAuth(
60-
baseUrl = vssUrl,
61-
storeId = vssStoreId,
62-
mnemonic = mnemonic,
63-
passphrase = passphraseSecret?.peek { String(it) },
64-
lnurlAuthServerUrl = lnurlAuthServerUrl,
65-
)
66-
}
67-
passphraseSecret?.wipe()
57+
vssNewClientWithLnurlAuth(
58+
baseUrl = vssUrl,
59+
storeId = vssStoreId,
60+
mnemonic = mnemonicSecret.use { String(it) },
61+
passphrase = passphraseSecret?.use { String(it) },
62+
lnurlAuthServerUrl = lnurlAuthServerUrl,
63+
)
6864
} else {
6965
mnemonicSecret.wipe()
7066
vssNewClient(

app/src/main/java/to/bitkit/data/backup/VssStoreIdProvider.kt

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package to.bitkit.data.backup
22

33
import com.synonym.vssclient.vssDeriveStoreId
44
import to.bitkit.data.keychain.Keychain
5-
import to.bitkit.domain.models.useAsString
65
import to.bitkit.env.Env
76
import to.bitkit.utils.Logger
87
import to.bitkit.utils.ServiceError
@@ -24,14 +23,11 @@ class VssStoreIdProvider @Inject constructor(
2423
?: throw ServiceError.MnemonicNotFound()
2524
val passphraseSecret = keychain.loadSecret(Keychain.Key.BIP39_PASSPHRASE.name)
2625

27-
val storeId = mnemonicSecret.useAsString { mnemonic ->
28-
vssDeriveStoreId(
29-
prefix = Env.vssStoreIdPrefix,
30-
mnemonic = mnemonic,
31-
passphrase = passphraseSecret?.peek { String(it) },
32-
)
33-
}
34-
passphraseSecret?.wipe()
26+
val storeId = vssDeriveStoreId(
27+
prefix = Env.vssStoreIdPrefix,
28+
mnemonic = mnemonicSecret.use { String(it) },
29+
passphrase = passphraseSecret?.use { String(it) },
30+
)
3531

3632
cacheMap[walletIndex] = storeId
3733
Logger.info("VSS store id setup for wallet[$walletIndex]: '$storeId'", context = TAG)

app/src/main/java/to/bitkit/domain/models/Secret.kt

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ private const val WIPE_CHAR = '\u0000'
77
/**
88
* A wrapper that stores sensitive data in a [CharArray] and provides APIs to safely wipe it from memory.
99
*
10+
* Implements [CharSequence] so it can flow through internal APIs without materializing a [String].
11+
* [toString] is intentionally redacted — use [peek] or [use] to access the underlying data.
12+
*
1013
* ALWAYS access the wrapped value inside [use] blocks for auto cleanup.
1114
*/
12-
class Secret internal constructor(initialValue: CharArray) : AutoCloseable {
15+
class Secret internal constructor(initialValue: CharArray) : CharSequence, AutoCloseable {
1316
companion object {
1417
const val ERR_WIPED = "Secret has already been wiped."
1518
}
@@ -21,6 +24,15 @@ class Secret internal constructor(initialValue: CharArray) : AutoCloseable {
2124
initialValue.wipe()
2225
}
2326

27+
override val length: Int get() = data?.size ?: 0
28+
29+
override fun get(index: Int): Char = checkNotNull(data) { ERR_WIPED }[index]
30+
31+
override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
32+
secretOf(checkNotNull(data) { ERR_WIPED }.copyOfRange(startIndex, endIndex))
33+
34+
override fun toString(): String = "Secret(***)"
35+
2436
internal operator fun getValue(thisRef: Any?, property: KProperty<*>): CharArray {
2537
return checkNotNull(data) { ERR_WIPED }
2638
}
@@ -55,9 +67,17 @@ fun secretOf(value: CharArray) = Secret(value)
5567

5668
fun secretOf(value: String): Secret = Secret(value.toCharArray())
5769

58-
internal inline fun <R> Secret.useAsString(block: (String) -> R): R = use { block(String(it)) }
59-
60-
internal fun Secret.splitWords(): List<Secret> =
61-
peek { chars -> String(chars).split(" ").filter { it.isNotBlank() }.map { secretOf(it) } }
70+
internal fun Secret.splitWords(): List<Secret> = peek { chars ->
71+
val words = mutableListOf<Secret>()
72+
var start = 0
73+
for (i in chars.indices) {
74+
if (chars[i] == ' ') {
75+
if (i > start) words.add(secretOf(chars.copyOfRange(start, i)))
76+
start = i + 1
77+
}
78+
}
79+
if (start < chars.size) words.add(secretOf(chars.copyOfRange(start, chars.size)))
80+
words
81+
}
6282

6383
internal fun List<Secret>.wipeAll() = forEach { it.wipe() }

app/src/main/java/to/bitkit/repositories/LightningRepo.kt

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ import to.bitkit.data.CacheStore
4747
import to.bitkit.data.SettingsStore
4848
import to.bitkit.data.keychain.Keychain
4949
import to.bitkit.di.BgDispatcher
50-
import to.bitkit.domain.models.useAsString
5150
import to.bitkit.env.Env
5251
import to.bitkit.ext.getSatsPerVByteFor
5352
import to.bitkit.ext.nowTimestamp
@@ -685,19 +684,15 @@ class LightningRepo @Inject constructor(
685684
?: throw ServiceError.MnemonicNotFound()
686685
val passphraseSecret = keychain.loadSecret(Keychain.Key.BIP39_PASSPHRASE.name)
687686

688-
mnemonicSecret.useAsString { mnemonic ->
689-
lnurlAuth(
690-
k1 = k1,
691-
callback = callback,
692-
domain = domain,
693-
network = Env.network.toCoreNetwork(),
694-
bip32Mnemonic = mnemonic,
695-
bip39Passphrase = passphraseSecret?.peek { String(it) },
696-
).also {
697-
Logger.debug("LNURL auth result: '$it'", context = TAG)
698-
}
699-
}.also {
700-
passphraseSecret?.wipe()
687+
lnurlAuth(
688+
k1 = k1,
689+
callback = callback,
690+
domain = domain,
691+
network = Env.network.toCoreNetwork(),
692+
bip32Mnemonic = mnemonicSecret.use { String(it) },
693+
bip39Passphrase = passphraseSecret?.use { String(it) },
694+
).also {
695+
Logger.debug("LNURL auth result: '$it'", context = TAG)
701696
}
702697
}.onFailure {
703698
Logger.error("requestLnurlAuth error, k1: $k1, callback: $callback, domain: $domain", it, context = TAG)

app/src/main/java/to/bitkit/repositories/SweepRepo.kt

Lines changed: 24 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import kotlinx.coroutines.withContext
99
import to.bitkit.async.ServiceQueue
1010
import to.bitkit.data.keychain.Keychain
1111
import to.bitkit.di.BgDispatcher
12-
import to.bitkit.domain.models.useAsString
1312
import to.bitkit.env.Env
1413
import to.bitkit.models.toCoreNetwork
1514
import to.bitkit.services.CoreService
@@ -38,17 +37,14 @@ class SweepRepo @Inject constructor(
3837

3938
Logger.debug("Checking sweepable balances...", context = TAG)
4039

41-
val balances = mnemonicSecret.useAsString { mnemonic ->
42-
ServiceQueue.CORE.background {
43-
checkSweepableBalances(
44-
mnemonicPhrase = mnemonic,
45-
network = Env.network.toCoreNetwork(),
46-
bip39Passphrase = passphraseSecret?.peek { String(it) },
47-
electrumUrl = Env.electrumServerUrl,
48-
)
49-
}
40+
val balances = ServiceQueue.CORE.background {
41+
checkSweepableBalances(
42+
mnemonicPhrase = mnemonicSecret.use { String(it) },
43+
network = Env.network.toCoreNetwork(),
44+
bip39Passphrase = passphraseSecret?.use { String(it) },
45+
electrumUrl = Env.electrumServerUrl,
46+
)
5047
}
51-
passphraseSecret?.wipe()
5248

5349
balances.toSweepableBalances()
5450
}
@@ -65,19 +61,16 @@ class SweepRepo @Inject constructor(
6561

6662
Logger.debug("Preparing sweep transaction...", context = TAG)
6763

68-
val preview = mnemonicSecret.useAsString { mnemonic ->
69-
ServiceQueue.CORE.background {
70-
prepareSweepTransaction(
71-
mnemonicPhrase = mnemonic,
72-
network = Env.network.toCoreNetwork(),
73-
bip39Passphrase = passphraseSecret?.peek { String(it) },
74-
electrumUrl = Env.electrumServerUrl,
75-
destinationAddress = destinationAddress,
76-
feeRateSatsPerVbyte = feeRateSatsPerVbyte,
77-
)
78-
}
64+
val preview = ServiceQueue.CORE.background {
65+
prepareSweepTransaction(
66+
mnemonicPhrase = mnemonicSecret.use { String(it) },
67+
network = Env.network.toCoreNetwork(),
68+
bip39Passphrase = passphraseSecret?.use { String(it) },
69+
electrumUrl = Env.electrumServerUrl,
70+
destinationAddress = destinationAddress,
71+
feeRateSatsPerVbyte = feeRateSatsPerVbyte,
72+
)
7973
}
80-
passphraseSecret?.wipe()
8174

8275
preview.toSweepTransactionPreview()
8376
}
@@ -91,18 +84,15 @@ class SweepRepo @Inject constructor(
9184

9285
Logger.debug("Broadcasting sweep transaction...", context = TAG)
9386

94-
val result = mnemonicSecret.useAsString { mnemonic ->
95-
ServiceQueue.CORE.background {
96-
broadcastSweepTransaction(
97-
psbt = psbt,
98-
mnemonicPhrase = mnemonic,
99-
network = Env.network.toCoreNetwork(),
100-
bip39Passphrase = passphraseSecret?.peek { String(it) },
101-
electrumUrl = Env.electrumServerUrl,
102-
)
103-
}
87+
val result = ServiceQueue.CORE.background {
88+
broadcastSweepTransaction(
89+
psbt = psbt,
90+
mnemonicPhrase = mnemonicSecret.use { String(it) },
91+
network = Env.network.toCoreNetwork(),
92+
bip39Passphrase = passphraseSecret?.use { String(it) },
93+
electrumUrl = Env.electrumServerUrl,
94+
)
10495
}
105-
passphraseSecret?.wipe()
10696

10797
result.toSweepResult()
10898
}

app/src/main/java/to/bitkit/repositories/WalletRepo.kt

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ import to.bitkit.data.keychain.Keychain
2323
import to.bitkit.di.BgDispatcher
2424
import to.bitkit.domain.models.Secret
2525
import to.bitkit.domain.models.secretOf
26-
import to.bitkit.domain.models.useAsString
2726
import to.bitkit.env.Env
2827
import to.bitkit.ext.filterOpen
2928
import to.bitkit.ext.nowTimestamp
@@ -345,18 +344,15 @@ class WalletRepo @Inject constructor(
345344
isChange = isChange,
346345
).substringBeforeLast("/0")
347346

348-
val result = mnemonicSecret.useAsString { mnemonic ->
349-
coreService.onchain.deriveBitcoinAddresses(
350-
mnemonicPhrase = mnemonic,
351-
derivationPathStr = baseDerivationPath,
352-
network = Env.network,
353-
bip39Passphrase = passphraseSecret?.peek { String(it) },
354-
isChange = isChange,
355-
startIndex = startIndex.toUInt(),
356-
count = count.toUInt(),
357-
)
358-
}
359-
passphraseSecret?.wipe()
347+
val result = coreService.onchain.deriveBitcoinAddresses(
348+
mnemonicPhrase = mnemonicSecret,
349+
derivationPathStr = baseDerivationPath,
350+
network = Env.network,
351+
bip39Passphrase = passphraseSecret,
352+
isChange = isChange,
353+
startIndex = startIndex.toUInt(),
354+
count = count.toUInt(),
355+
)
360356

361357
val addresses = result.addresses.mapIndexed { index, address ->
362358
AddressModel(

app/src/main/java/to/bitkit/services/CoreService.kt

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ import org.lightningdevkit.ldknode.PaymentStatus
6969
import org.lightningdevkit.ldknode.TransactionDetails
7070
import to.bitkit.async.ServiceQueue
7171
import to.bitkit.data.CacheStore
72+
import to.bitkit.domain.models.Secret
7273
import to.bitkit.env.Env
7374
import to.bitkit.ext.amountSats
7475
import to.bitkit.ext.create
@@ -1446,37 +1447,37 @@ class OnchainService {
14461447
}
14471448

14481449
suspend fun deriveBitcoinAddress(
1449-
mnemonicPhrase: String,
1450+
mnemonicPhrase: Secret,
14501451
derivationPathStr: String?,
14511452
network: Network?,
1452-
bip39Passphrase: String?,
1453+
bip39Passphrase: Secret?,
14531454
): GetAddressResponse {
14541455
return ServiceQueue.CORE.background {
14551456
com.synonym.bitkitcore.deriveBitcoinAddress(
1456-
mnemonicPhrase = mnemonicPhrase,
1457+
mnemonicPhrase = mnemonicPhrase.use { String(it) },
14571458
derivationPathStr = derivationPathStr,
14581459
network = network?.toCoreNetwork(),
1459-
bip39Passphrase = bip39Passphrase,
1460+
bip39Passphrase = bip39Passphrase?.use { String(it) },
14601461
)
14611462
}
14621463
}
14631464

14641465
@Suppress("LongParameterList")
14651466
suspend fun deriveBitcoinAddresses(
1466-
mnemonicPhrase: String,
1467+
mnemonicPhrase: Secret,
14671468
derivationPathStr: String?,
14681469
network: Network?,
1469-
bip39Passphrase: String?,
1470+
bip39Passphrase: Secret?,
14701471
isChange: Boolean?,
14711472
startIndex: UInt?,
14721473
count: UInt?,
14731474
): GetAddressesResponse {
14741475
return ServiceQueue.CORE.background {
1475-
return@background com.synonym.bitkitcore.deriveBitcoinAddresses(
1476-
mnemonicPhrase = mnemonicPhrase,
1476+
com.synonym.bitkitcore.deriveBitcoinAddresses(
1477+
mnemonicPhrase = mnemonicPhrase.use { String(it) },
14771478
derivationPathStr = derivationPathStr,
14781479
network = network?.toCoreNetwork(),
1479-
bip39Passphrase = bip39Passphrase,
1480+
bip39Passphrase = bip39Passphrase?.use { String(it) },
14801481
isChange = isChange,
14811482
startIndex = startIndex,
14821483
count = count,
@@ -1485,17 +1486,17 @@ class OnchainService {
14851486
}
14861487

14871488
suspend fun derivePrivateKey(
1488-
mnemonicPhrase: String,
1489+
mnemonicPhrase: Secret,
14891490
derivationPathStr: String?,
14901491
network: Network?,
1491-
bip39Passphrase: String?,
1492+
bip39Passphrase: Secret?,
14921493
): String {
14931494
return ServiceQueue.CORE.background {
14941495
com.synonym.bitkitcore.derivePrivateKey(
1495-
mnemonicPhrase = mnemonicPhrase,
1496+
mnemonicPhrase = mnemonicPhrase.use { String(it) },
14961497
derivationPathStr = derivationPathStr,
14971498
network = network?.toCoreNetwork(),
1498-
bip39Passphrase = bip39Passphrase,
1499+
bip39Passphrase = bip39Passphrase?.use { String(it) },
14991500
)
15001501
}
15011502
}

app/src/main/java/to/bitkit/services/LightningService.kt

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import to.bitkit.data.SettingsStore
4343
import to.bitkit.data.backup.VssStoreIdProvider
4444
import to.bitkit.data.keychain.Keychain
4545
import to.bitkit.di.BgDispatcher
46-
import to.bitkit.domain.models.useAsString
4746
import to.bitkit.env.Env
4847
import to.bitkit.ext.totalNextOutboundHtlcLimitSats
4948
import to.bitkit.ext.uByteList
@@ -152,13 +151,10 @@ class LightningService @Inject constructor(
152151
?: throw ServiceError.MnemonicNotFound()
153152
val passphraseSecret = keychain.loadSecret(Keychain.Key.BIP39_PASSPHRASE.name)
154153

155-
mnemonicSecret.useAsString { mnemonic ->
156-
setEntropyBip39Mnemonic(
157-
mnemonic = mnemonic,
158-
passphrase = passphraseSecret?.peek { String(it) },
159-
)
160-
}
161-
passphraseSecret?.wipe()
154+
setEntropyBip39Mnemonic(
155+
mnemonic = mnemonicSecret.use { String(it) },
156+
passphrase = passphraseSecret?.use { String(it) },
157+
)
162158
}
163159
try {
164160
val vssStoreId = vssStoreIdProvider.getVssStoreId(walletIndex)

0 commit comments

Comments
 (0)