diff options
Diffstat (limited to 'vespaclient-java/src/main/java/com/yahoo')
-rw-r--r-- | vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java | 49 | ||||
-rw-r--r-- | vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java | 55 |
2 files changed, 85 insertions, 19 deletions
diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java index 4fbe89d4b03..4b3608fc3f7 100644 --- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java +++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/DecryptTool.java @@ -2,14 +2,18 @@ package com.yahoo.vespa.security.tool.crypto; import com.yahoo.security.SealedSharedKey; +import com.yahoo.security.SecretSharedKey; import com.yahoo.security.SharedKeyGenerator; +import com.yahoo.security.SharedKeyResealingSession; import com.yahoo.vespa.security.tool.CliUtils; import com.yahoo.vespa.security.tool.Tool; import com.yahoo.vespa.security.tool.ToolDescription; import com.yahoo.vespa.security.tool.ToolInvocation; import org.apache.commons.cli.Option; +import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStreamReader; import java.util.List; import java.util.Optional; @@ -31,6 +35,7 @@ public class DecryptTool implements Tool { static final String EXPECTED_KEY_ID_OPTION = "expected-key-id"; static final String ZSTD_DECOMPRESS_OPTION = "zstd-decompress"; static final String TOKEN_OPTION = "token"; + static final String RESEAL_REQUEST = "reseal-request"; private static final List<Option> OPTIONS = List.of( Option.builder("o") @@ -77,6 +82,12 @@ public class DecryptTool implements Tool { .hasArg(true) .required(false) .desc("Token generated when the input file was encrypted") + .build(), + Option.builder("r") + .longOpt(RESEAL_REQUEST) + .hasArg(false) + .required(false) + .desc("Delegate private key decryption via an interactive resealing session") .build()); @Override @@ -110,11 +121,12 @@ public class DecryptTool implements Tool { var sealedSharedKey = SealedSharedKey.fromTokenString(tokenString.strip()); ToolUtils.verifyExpectedKeyId(sealedSharedKey, maybeKeyId); - var privateKey = ToolUtils.resolvePrivateKeyFromInvocation(invocation, sealedSharedKey.keyId(), - !CliUtils.useStdIo(inputArg) && !CliUtils.useStdIo(outputArg)); - var secretShared = SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey); - var cipher = secretShared.makeDecryptionCipher(); - boolean unZstd = arguments.hasOption(ZSTD_DECOMPRESS_OPTION); + var secret = arguments.hasOption(RESEAL_REQUEST) + ? secretFromInteractiveResealing(invocation, inputArg, outputArg, sealedSharedKey) + : secretFromPrivateKey(invocation, inputArg, outputArg, sealedSharedKey); + + var cipher = secret.makeDecryptionCipher(); + boolean unZstd = arguments.hasOption(ZSTD_DECOMPRESS_OPTION); try (var inStream = CliUtils.inputStreamFromFileOrStream(inputArg, invocation.stdIn()); var outStream = CliUtils.outputStreamToFileOrStream(outputArg, invocation.stdOut())) { @@ -125,4 +137,31 @@ public class DecryptTool implements Tool { } return 0; } + + private static SecretSharedKey secretFromPrivateKey(ToolInvocation invocation, String inputArg, String outputArg, SealedSharedKey sealedSharedKey) throws IOException { + var privateKey = ToolUtils.resolvePrivateKeyFromInvocation(invocation, sealedSharedKey.keyId(), + !CliUtils.useStdIo(inputArg) && !CliUtils.useStdIo(outputArg)); + return SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey); + } + + private static SecretSharedKey secretFromInteractiveResealing(ToolInvocation invocation, String inputArg, + String outputArg, SealedSharedKey sealedSharedKey) throws IOException { + if (!CliUtils.useStdIo(outputArg) || !CliUtils.useStdIo(inputArg)) { + throw new IllegalArgumentException("Interactive token resealing not available with redirected I/O"); + } + var session = SharedKeyResealingSession.newEphemeralSession(); + var req = session.resealingRequestFor(sealedSharedKey); + + invocation.stdOut().format("\nInteractive token resealing request:\n\n%s\n\n", req.toSerializedString()); + invocation.stdOut().format("Paste response and hit return: "); + + try (var reader = new BufferedReader(new InputStreamReader(invocation.stdIn()))) { + var serializedRes = reader.readLine().strip(); + if (serializedRes.isEmpty()) { + throw new IllegalArgumentException("Empty response; aborting"); + } + var res = SharedKeyResealingSession.ResealingResponse.fromSerializedString(serializedRes); + return session.openResealingResponse(res); + } + } } diff --git a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java index 19be3e9fa51..4fb8083b0f0 100644 --- a/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java +++ b/vespaclient-java/src/main/java/com/yahoo/vespa/security/tool/crypto/ResealTool.java @@ -5,10 +5,12 @@ import com.yahoo.security.KeyId; import com.yahoo.security.KeyUtils; import com.yahoo.security.SealedSharedKey; import com.yahoo.security.SharedKeyGenerator; +import com.yahoo.security.SharedKeyResealingSession; import com.yahoo.vespa.security.tool.CliUtils; import com.yahoo.vespa.security.tool.Tool; import com.yahoo.vespa.security.tool.ToolDescription; import com.yahoo.vespa.security.tool.ToolInvocation; +import org.apache.commons.cli.CommandLine; import org.apache.commons.cli.Option; import java.io.IOException; @@ -31,6 +33,7 @@ public class ResealTool implements Tool { static final String EXPECTED_KEY_ID_OPTION = "expected-key-id"; static final String RECIPIENT_KEY_ID_OPTION = "key-id"; static final String RECIPIENT_PUBLIC_KEY_OPTION = "recipient-public-key"; + static final String RESEAL_REQUEST_OPTION = "reseal-request"; private static final List<Option> OPTIONS = List.of( Option.builder("k") @@ -70,6 +73,12 @@ public class ResealTool implements Tool { .hasArg(true) .required(false) .desc("ID of recipient key") + .build(), + Option.builder() + .longOpt(RESEAL_REQUEST_OPTION) + .hasArg(false) + .required(false) + .desc("Handle input as a resealing request instead of a token") .build()); @Override @@ -96,23 +105,41 @@ public class ResealTool implements Tool { if (leftoverArgs.length != 1) { throw new IllegalArgumentException("Expected exactly 1 token argument to re-seal"); } - var tokenString = leftoverArgs[0]; - var maybeKeyId = Optional.ofNullable(arguments.hasOption(EXPECTED_KEY_ID_OPTION) - ? arguments.getOptionValue(EXPECTED_KEY_ID_OPTION) - : null); - var sealedSharedKey = SealedSharedKey.fromTokenString(tokenString.strip()); - ToolUtils.verifyExpectedKeyId(sealedSharedKey, maybeKeyId); - - var recipientPubKey = KeyUtils.fromBase58EncodedX25519PublicKey(CliUtils.optionOrThrow(arguments, RECIPIENT_PUBLIC_KEY_OPTION).strip()); - var recipientKeyId = KeyId.ofString(CliUtils.optionOrThrow(arguments, RECIPIENT_KEY_ID_OPTION)); - var privateKey = ToolUtils.resolvePrivateKeyFromInvocation(invocation, sealedSharedKey.keyId(), true); - var secretShared = SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey); - var resealedShared = SharedKeyGenerator.reseal(secretShared, recipientPubKey, recipientKeyId); - - invocation.stdOut().println(resealedShared.sealedSharedKey().toTokenString()); + var inputArg = leftoverArgs[0].strip(); + var maybeKeyId = Optional.ofNullable(arguments.hasOption(EXPECTED_KEY_ID_OPTION) + ? arguments.getOptionValue(EXPECTED_KEY_ID_OPTION) + : null); + if (arguments.hasOption(RESEAL_REQUEST_OPTION)) { + handleResealingRequest(invocation, inputArg, maybeKeyId); + } else { + handleTokenResealing(invocation, arguments, inputArg, maybeKeyId); + } } catch (IOException e) { throw new RuntimeException(e); } return 0; } + + private static void handleTokenResealing(ToolInvocation invocation, CommandLine arguments, String inputArg, Optional<String> maybeKeyId) throws IOException { + var sealedSharedKey = SealedSharedKey.fromTokenString(inputArg); + ToolUtils.verifyExpectedKeyId(sealedSharedKey, maybeKeyId); + + var recipientPubKey = KeyUtils.fromBase58EncodedX25519PublicKey(CliUtils.optionOrThrow(arguments, RECIPIENT_PUBLIC_KEY_OPTION).strip()); + var recipientKeyId = KeyId.ofString(CliUtils.optionOrThrow(arguments, RECIPIENT_KEY_ID_OPTION)); + var privateKey = ToolUtils.resolvePrivateKeyFromInvocation(invocation, sealedSharedKey.keyId(), true); + var secretShared = SharedKeyGenerator.fromSealedKey(sealedSharedKey, privateKey); + var resealedShared = SharedKeyGenerator.reseal(secretShared, recipientPubKey, recipientKeyId); + + invocation.stdOut().println(resealedShared.sealedSharedKey().toTokenString()); + } + + private static void handleResealingRequest(ToolInvocation invocation, String inputArg, Optional<String> maybeKeyId) throws IOException { + var request = SharedKeyResealingSession.ResealingRequest.fromSerializedString(inputArg); + ToolUtils.verifyExpectedKeyId(request.sealedKey(), maybeKeyId); + + var privateKey = ToolUtils.resolvePrivateKeyFromInvocation(invocation, request.sealedKey().keyId(), true); + var resealed = SharedKeyResealingSession.reseal(request, (keyId) -> Optional.of(privateKey)); + + invocation.stdOut().println(resealed.toSerializedString()); + } } |