Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,7 @@ internal class ToolSecretsCommand : ProjectAwareSubcommand<ToolState, CommandCon
ManageRemoteOptions.CREATE -> createAccess(remote)
ManageRemoteOptions.LIST -> println(remote.listAccesses())
ManageRemoteOptions.SELECT -> selectAccess(remote)
ManageRemoteOptions.CHANGE -> remote.changeSuperEncryption()
ManageRemoteOptions.REMOVE -> removeAccess(remote)
ManageRemoteOptions.REKEY -> rekeyProfiles(remote)
ManageRemoteOptions.DELETE -> deleteProfile(remote)
Expand Down Expand Up @@ -379,6 +380,7 @@ internal class ToolSecretsCommand : ProjectAwareSubcommand<ToolState, CommandCon
CREATE("Create an access file"),
LIST("List access files"),
SELECT("Select an access file"),
CHANGE("Change superuser encryption details"),
REMOVE("Remove an access file"),
REKEY("Regenerate profile keys"),
DELETE("Delete a profile"),
Expand Down
1 change: 1 addition & 0 deletions packages/secrets/api/secrets.api
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
public abstract interface class elide/secrets/RemoteManagement {
public abstract fun addProfile (Ljava/lang/String;)V
public abstract fun changeEncryption ()V
public abstract fun changeSuperEncryption ()V
public abstract fun createAccess (Ljava/lang/String;)V
public abstract fun deleteAccess (Ljava/lang/String;)V
public abstract fun deleteProfile (Ljava/lang/String;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ public interface RemoteManagement {
/** Deselects the selected access file. */
public fun deselectAccess()

/** Prompts user on a new encryption mode for superuser access. */
public fun changeSuperEncryption()

/** Generates new keys for specified profiles. */
public fun rekeyProfile(profile: String)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ internal object SecretValues {
const val DELETE_PROFILES_MESSAGE = "Deleting the profile will also delete it from your local secrets!"
const val GPG_KEY_REVOKED_MISSING_MESSAGE =
"GPG private key is expired, revoked or not present. If secrets load correctly, please update your encryption mode!"
const val INVALID_SUPER_CREDENTIALS_MESSAGE = "Invalid superuser credentials."

fun accessesWithProfileMessage(accesses: String) =
"The following access files contain the profile:\n$accesses\n" +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,16 @@ internal class RemoteManagementImpl(
access = null
}

override fun changeSuperEncryption() {
val mode = SecretPrompts.accessMode(prompts)
superKey = when (mode) {
EncryptionMode.PASSPHRASE ->
UserKey(encryption.hashKeySHA256(SecretPrompts.passphrase(prompts).encodeToByteString()))

EncryptionMode.GPG -> UserKey(SecretPrompts.gpgPrivateKey())
}
}

override fun rekeyProfile(profile: String) {
rekeyed.add(profile)
}
Expand Down Expand Up @@ -212,7 +222,9 @@ internal class RemoteManagementImpl(
)
deleted.forEach { files.removeProfile(it) }
SecretsState.updateMetadata { removeAll(deleted) }
SecretsState.updateLocal { add(BinarySecret(SecretValues.SUPER_ACCESS_KEY_SECRET, superKey.key)) }
files.writeMetadata()
files.writeLocal()
}

private fun rekeyProfiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import java.nio.file.Path
import kotlinx.io.bytestring.ByteString
import kotlinx.io.files.SystemFileSystem
import kotlinx.serialization.BinaryFormat
import kotlinx.serialization.SerializationException
import kotlinx.serialization.json.Json
import elide.annotations.Singleton
import elide.secrets.*
Expand Down Expand Up @@ -291,8 +292,20 @@ internal class SecretManagementImpl(
)
EncryptionMode.GPG -> UserKey(remoteMetadata.superAccess.fingerprint!!)
}
val superAccess: SuperAccess =
val superAccess: SuperAccess = try {
SecretsState.remote.getSuperAccess()!!.decrypt(superKey, encryption).deserialize(cbor)
} catch(_: SerializationException) {
println(SecretValues.INVALID_SUPER_CREDENTIALS_MESSAGE)
val superKey: UserKey =
when (remoteMetadata.superAccess.mode) {
EncryptionMode.PASSPHRASE ->
UserKey(prompts.removeFirstOrNull()?.hashKey(encryption)
?: KInquirer.promptInputPassword(SecretValues.SUPERUSER_PASSPHRASE_PROMPT).hashKey(encryption),
)
EncryptionMode.GPG -> UserKey(remoteMetadata.superAccess.fingerprint!!)
}
SecretsState.remote.getSuperAccess()!!.decrypt(superKey, encryption).deserialize(cbor)
}
return RemoteManagementImpl(secrets, files, encryption, json, cbor, remoteMetadata, superAccess, superKey, prompts)
.apply {
init()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,6 +254,11 @@ class ManagementTest : AbstractSecretTest() {
// regenerate profile key
remote.rekeyProfile("test")

// change superuser access passphrase
// remote.changeSuperEncryption() asks for encryption mode and passphrase twice.
queuePrompts(EncryptionMode.PASSPHRASE, "seus", "seus")
remote.changeSuperEncryption()

// push changes and check for files
remote.push()
assertTrue(path.resolve(SecretValues.PROJECT_REMOTE_DEFAULT_PATH).resolve(SecretValues.METADATA_FILE).exists())
Expand Down
Loading