Skip to content

Commit

Permalink
Refactor commands with Key.Private to always have `destroyKeyOnJobC…
Browse files Browse the repository at this point in the history
…ompletion` as an option (#435)
  • Loading branch information
05nelsonm authored Jun 9, 2024
1 parent 52e93e8 commit ee49ebb
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 58 deletions.
4 changes: 3 additions & 1 deletion library/runtime-core/api/runtime-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -3366,6 +3366,7 @@ public final class io/matthewnelson/kmp/tor/runtime/core/builder/IsolationFlagBu
}

public final class io/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder {
public field destroyKeyOnJobCompletion Z
public final fun clientAuth (Lio/matthewnelson/kmp/tor/runtime/core/key/X25519$PublicKey;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
public final fun flags (Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
public final fun maxStreams (Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder;
Expand All @@ -3380,6 +3381,7 @@ public final class io/matthewnelson/kmp/tor/runtime/core/builder/OnionAddBuilder

public final class io/matthewnelson/kmp/tor/runtime/core/builder/OnionClientAuthAddBuilder {
public field clientName Ljava/lang/String;
public field destroyKeyOnJobCompletion Z
public final fun flags (Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)Lio/matthewnelson/kmp/tor/runtime/core/builder/OnionClientAuthAddBuilder;
}

Expand Down Expand Up @@ -3685,7 +3687,6 @@ public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$Onion$Add :
public final field maxStreams Lio/matthewnelson/kmp/tor/runtime/core/TorConfig$LineItem;
public final field ports Ljava/util/Set;
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/key/AddressKey$Private;Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)V
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/key/AddressKey$Private;ZLio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)V
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/key/KeyType$Address;Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)V
}

Expand All @@ -3706,6 +3707,7 @@ public final class io/matthewnelson/kmp/tor/runtime/core/ctrl/TorCmd$OnionClient
public final field address Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress;
public final field authKey Lio/matthewnelson/kmp/tor/runtime/core/key/AuthKey$Private;
public final field clientName Ljava/lang/String;
public final field destroyKeyOnJobCompletion Z
public final field flags Ljava/util/Set;
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress$V3;Lio/matthewnelson/kmp/tor/runtime/core/key/X25519$PrivateKey;)V
public fun <init> (Lio/matthewnelson/kmp/tor/runtime/core/address/OnionAddress$V3;Lio/matthewnelson/kmp/tor/runtime/core/key/X25519$PrivateKey;Lio/matthewnelson/kmp/tor/runtime/core/ThisBlock;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,15 @@ package io.matthewnelson.kmp.tor.runtime.core.builder

import io.matthewnelson.immutable.collections.toImmutableSet
import io.matthewnelson.kmp.tor.core.api.annotation.KmpTorDsl
import io.matthewnelson.kmp.tor.runtime.core.EnqueuedJob
import io.matthewnelson.kmp.tor.runtime.core.ThisBlock
import io.matthewnelson.kmp.tor.runtime.core.TorConfig
import io.matthewnelson.kmp.tor.runtime.core.TorConfig.HiddenServiceMaxStreams
import io.matthewnelson.kmp.tor.runtime.core.TorConfig.HiddenServicePort
import io.matthewnelson.kmp.tor.runtime.core.apply
import io.matthewnelson.kmp.tor.runtime.core.ctrl.HiddenServiceEntry
import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd
import io.matthewnelson.kmp.tor.runtime.core.key.AddressKey
import io.matthewnelson.kmp.tor.runtime.core.key.AuthKey
import io.matthewnelson.kmp.tor.runtime.core.key.KeyType
import io.matthewnelson.kmp.tor.runtime.core.key.X25519
Expand All @@ -37,7 +39,7 @@ import kotlin.jvm.JvmSynthetic
*
* Tor's implementation of the ADD_ONION control command
* is extremely overloaded with several "gotchas". This
* attempts to alleviate those problems.
* attempts to alleviate some pain points.
*
* **NOTE:** A minimum of 1 [port] **must** be declared for
* the call to succeed (i.e. [HiddenServicePort.virtual] must
Expand All @@ -60,6 +62,8 @@ import kotlin.jvm.JvmSynthetic
* .resolve("test_hs.sock")
* }
* } catch (_: UnsupportedOperationException) {
* // Fallback to TCP if on system w/o
* // UnixSocket support.
* targetAsPort { target = 8443.toPort() }
* }
* }
Expand All @@ -73,10 +77,7 @@ import kotlin.jvm.JvmSynthetic
* //
* // entry.privateKey will not be null because `DiscardPK`
* // flag was not defined when created above.
* runtime.executeAsync(TorCmd.Onion.Add(
* addressKey = entry.privateKey!!,
* destroyKeyOnJobCompletion = true,
* ) {
* runtime.executeAsync(TorCmd.Onion.Add(entry.privateKey!!) {
* port {
* virtual = 80.toPort()
* targetAsPort { target = 8080.toPort() }
Expand All @@ -99,6 +100,21 @@ public class OnionAddBuilder private constructor() {
private var maxStreams: TorConfig.LineItem? = null
private val ports = LinkedHashSet<TorConfig.LineItem>(1, 1.0f)

/**
* When true, an [EnqueuedJob.invokeOnCompletion] handler is
* automatically set which calls [AddressKey.Private.destroy]
* once the job completes, either successfully or by
* cancellation/error.
*
* Default = `true`
*
* This setting has no effect if [TorCmd.Onion.Add] is being
* instantiated using [KeyType.Address] for new HiddenService
* creation, as there will be no [AddressKey.Private] present.
* */
@JvmField
public var destroyKeyOnJobCompletion: Boolean = true

@KmpTorDsl
public fun port(
block: ThisBlock<HiddenServicePort>,
Expand Down Expand Up @@ -190,21 +206,23 @@ public class OnionAddBuilder private constructor() {
val b = OnionAddBuilder().apply(block)

return Arguments(
keyType = this,
clientAuth = b.clientAuth,
destroyKeyOnJobCompletion = b.destroyKeyOnJobCompletion,
flags = b.flags,
ports = b.ports,
keyType = this,
maxStreams = b.maxStreams,
ports = b.ports,
)
}
}

internal class Arguments internal constructor(
internal val keyType: KeyType.Address<*, *>,
clientAuth: Set<AuthKey.Public>,
internal val destroyKeyOnJobCompletion: Boolean,
flags: Set<String>,
ports: Set<TorConfig.LineItem>,
internal val keyType: KeyType.Address<*, *>,
internal val maxStreams: TorConfig.LineItem?,
ports: Set<TorConfig.LineItem>,
) {

internal val clientAuth = clientAuth.toImmutableSet()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@ package io.matthewnelson.kmp.tor.runtime.core.builder

import io.matthewnelson.immutable.collections.toImmutableSet
import io.matthewnelson.kmp.tor.core.api.annotation.KmpTorDsl
import io.matthewnelson.kmp.tor.runtime.core.EnqueuedJob
import io.matthewnelson.kmp.tor.runtime.core.ThisBlock
import io.matthewnelson.kmp.tor.runtime.core.apply
import io.matthewnelson.kmp.tor.runtime.core.ctrl.TorCmd
import io.matthewnelson.kmp.tor.runtime.core.key.AuthKey
import kotlin.jvm.JvmField
import kotlin.jvm.JvmSynthetic

Expand All @@ -37,6 +39,17 @@ public class OnionClientAuthAddBuilder private constructor() {
@JvmField
public var clientName: String? = null

/**
* When true, an [EnqueuedJob.invokeOnCompletion] handler is
* automatically set which calls [AuthKey.Private.destroy]
* once the job completes, either successfully or by
* cancellation/error.
*
* Default = `true`
* */
@JvmField
public var destroyKeyOnJobCompletion: Boolean = true

@KmpTorDsl
public fun flags(
block: ThisBlock<FlagBuilder>,
Expand Down Expand Up @@ -74,6 +87,7 @@ public class OnionClientAuthAddBuilder private constructor() {
}
}
}

internal companion object {

@JvmSynthetic
Expand All @@ -82,20 +96,25 @@ public class OnionClientAuthAddBuilder private constructor() {
): Arguments {
val b = OnionClientAuthAddBuilder().apply(block)

return Arguments(b.clientName, b.flags)
return Arguments(
clientName = b.clientName,
destroyKeyOnJobCompletion = b.destroyKeyOnJobCompletion,
flags = b.flags,
)
}
}

internal class Arguments internal constructor(
internal val clientName: String?,
internal val destroyKeyOnJobCompletion: Boolean,
flags: Set<String>,
) {

internal val flags = flags.toImmutableSet()

internal companion object {

internal val EMPTY = Arguments(null, emptySet())
internal val EMPTY = Arguments(null, true, emptySet())
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -337,59 +337,30 @@ public sealed class TorCmd<Success: Any> private constructor(
public constructor(
keyType: KeyType.Address<*, *>,
block: ThisBlock<OnionAddBuilder>,
): this(null, false, keyType.configure(block))
): this(null, keyType.configure(block))

/**
* Creates an [Onion.Add] command that will instruct tor
* to add a HiddenService to its runtime for the provided
* [addressKey].
*
* **NOTE:** Default of [destroyKeyOnJobCompletion] = `true`
*
* e.g.
*
* TorCmd.Onion.Add("[Blob Redacted]".toED25519_V3PrivateKey()) {
* flags { DiscardPK = true }
* port { virtual = 80.toPort() }
* }
*
* @see [OnionAddBuilder]
* @see [ED25519_V3.PrivateKey]
* */
public constructor(
addressKey: AddressKey.Private,
block: ThisBlock<OnionAddBuilder>,
): this(addressKey, true, block)

/**
* Creates an [Onion.Add] command that will instruct tor
* to add a HiddenService to its runtime for the provided
* [addressKey].
*
* e.g.
*
* TorCmd.Onion.Add(
* addressKey = "[Blob Redacted]".toED25519_V3PrivateKey(),
* destroyKeyOnJobCompletion = false,
* ) {
* port { virtual = 80.toPort() }
* flags { DiscardPK = true }
* }
*
* @param [destroyKeyOnJobCompletion] If `true`, when the [EnqueuedJob]
* completes (either by success or cancellation/error) for this
* command, the provided [addressKey] will be destroyed automatically.
* @see [OnionAddBuilder]
* @see [ED25519_V3.PrivateKey]
* */
public constructor(
addressKey: AddressKey.Private,
destroyKeyOnJobCompletion: Boolean,
block: ThisBlock<OnionAddBuilder>,
): this(addressKey, destroyKeyOnJobCompletion, addressKey.type().configure(block))
): this(addressKey, addressKey.type().configure(block))

private constructor(
addressKey: AddressKey.Private?,
destroyKeyOnJobCompletion: Boolean,
arguments: OnionAddBuilder.Arguments,
): super("ADD_ONION") {
this.keyType = arguments.keyType
Expand All @@ -398,7 +369,7 @@ public sealed class TorCmd<Success: Any> private constructor(
this.flags = arguments.flags
this.maxStreams = arguments.maxStreams
this.ports = arguments.ports
this.destroyKeyOnJobCompletion = destroyKeyOnJobCompletion
this.destroyKeyOnJobCompletion = arguments.destroyKeyOnJobCompletion
}
}

Expand Down Expand Up @@ -436,6 +407,8 @@ public sealed class TorCmd<Success: Any> private constructor(
public val clientName: String?
@JvmField
public val flags: Set<String>
@JvmField
public val destroyKeyOnJobCompletion: Boolean

public constructor(
address: OnionAddress.V3,
Expand All @@ -457,6 +430,7 @@ public sealed class TorCmd<Success: Any> private constructor(
this.authKey = authKey
this.clientName = arguments.clientName
this.flags = arguments.flags
this.destroyKeyOnJobCompletion = arguments.destroyKeyOnJobCompletion
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,14 +80,19 @@ internal inline fun TorCmd<*>.toJobName(): String {
@Suppress("NOTHING_TO_INLINE")
internal inline fun <Job: EnqueuedJob> Job.invokeOnCompletionForCmd(
cmd: TorCmd<*>,
): Job {
if (cmd is TorCmd.Onion.Add) {
): Job = when (cmd) {
is TorCmd.Onion.Add -> {
val key = cmd.addressKey

if (key != null && cmd.destroyKeyOnJobCompletion) {
invokeOnCompletion { key.destroy() }
}
this
}

return this
is TorCmd.OnionClientAuth.Add -> {
if (cmd.destroyKeyOnJobCompletion) {
invokeOnCompletion { cmd.authKey.destroy() }
}
this
}
else -> this
}
Original file line number Diff line number Diff line change
Expand Up @@ -215,10 +215,10 @@ class TorCmdUnitTest {

runtime.executeAsync(TorCmd.Onion.Delete(entry1.publicKey))

val entry3 = runtime.executeAsync(TorCmd.Onion.Add(
addressKey = keyCopy,
destroyKeyOnJobCompletion = false,
) {
val entry3 = runtime.executeAsync(TorCmd.Onion.Add(keyCopy) {

destroyKeyOnJobCompletion = false

for (keys in authKeys) {
clientAuth(keys.first)
}
Expand Down Expand Up @@ -247,7 +247,7 @@ class TorCmdUnitTest {
if (line.contains("x25519:[REDACTED]")) {
containsRedacted = true
}
println(line)
// println(line)
}
}.ensureStoppedOnTestCompletion()

Expand Down Expand Up @@ -282,7 +282,10 @@ class TorCmdUnitTest {
entry.publicKey.address() as OnionAddress.V3,
// PrivateKey
authKeys.first().second,
) { clientName = nickname }
) {
clientName = nickname
destroyKeyOnJobCompletion = false
}
)

assertTrue(containsRedacted)
Expand All @@ -300,8 +303,10 @@ class TorCmdUnitTest {
TorCmd.OnionClientAuth.Add(
entry.publicKey.address() as OnionAddress.V3,
authKeys.last().second,
block
)
) {
destroyKeyOnJobCompletion = false
this.apply(block)
}
)
}
}
Expand Down

0 comments on commit ee49ebb

Please sign in to comment.