diff options
author | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-10-31 16:33:17 +0100 |
---|---|---|
committer | Tor Brede Vekterli <vekterli@yahooinc.com> | 2022-10-31 16:33:17 +0100 |
commit | c1f6b93a94d8bb75707cb2643abf026eca45be7d (patch) | |
tree | 18926d0b6c0001742dbc18a102e9cf0c826a559d /vespaclient-java/src/test | |
parent | b3adc4839d28db03dc675ca18d95b9bf71a4c465 (diff) |
Support standard IO streams for several encryption tool commands
Useful for avoiding the need for intermediate files, such as when
piping the output of decryption to a Zstd decompressor.
Adds stdio support to:
* Encryption input
* Decryption input
* Decryption output
Specified by substituting the file name with a single `-` character.
Diffstat (limited to 'vespaclient-java/src/test')
3 files changed, 74 insertions, 9 deletions
diff --git a/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java b/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java index bb8024544cb..91ca30fd564 100644 --- a/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java +++ b/vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java @@ -4,6 +4,7 @@ package com.yahoo.vespa.security.tool; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.io.TempDir; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.IOException; @@ -15,6 +16,7 @@ import java.nio.file.attribute.PosixFilePermissions; import java.util.List; import java.util.Map; +import static com.yahoo.security.ArrayUtils.toUtf8Bytes; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; @@ -27,17 +29,19 @@ public class CryptoToolsTest { private static record ProcessOutput(int exitCode, String stdOut, String stdErr) {} + private static final byte[] EMPTY_BYTES = new byte[0]; + @TempDir public File tmpFolder; private void verifyStdoutMatchesFile(List<String> args, String expectedFile) throws IOException { - var procOut = runMain(args, Map.of()); + var procOut = runMain(args, EMPTY_BYTES, Map.of()); assertEquals(0, procOut.exitCode()); assertEquals(readTestResource(expectedFile), procOut.stdOut()); } private void verifyStderrEquals(List<String> args, String expectedMessage) throws IOException { - var procOut = runMain(args, Map.of()); + var procOut = runMain(args, EMPTY_BYTES, Map.of()); assertEquals(1, procOut.exitCode()); // Assume checking stderr is because of a failure. assertEquals(expectedMessage, procOut.stdErr()); } @@ -161,7 +165,7 @@ public class CryptoToolsTest { "--output-file", "foo", "--recipient-public-key", TEST_PUB_KEY, "--key-id", "1234"), - "Invalid command line arguments: Cannot encrypt file 'no-such-file' as it does not exist\n"); + "Invalid command line arguments: Input file 'no-such-file' does not exist\n"); } @Test @@ -188,7 +192,7 @@ public class CryptoToolsTest { "--recipient-private-key-file", absPathOf(privKeyFile), "--token", TEST_TOKEN, "--key-id", Integer.toString(TEST_TOKEN_KEY_ID)), - "Invalid command line arguments: Cannot decrypt file 'no-such-file' as it does not exist\n"); + "Invalid command line arguments: Input file 'no-such-file' does not exist\n"); } @Test @@ -210,7 +214,7 @@ public class CryptoToolsTest { } @Test - void can_end_to_end_keygen_encrypt_and_decrypt() throws IOException { + void can_end_to_end_keygen_encrypt_and_decrypt_via_files() throws IOException { String greatSecret = "Dogs can't look up"; Path secretFile = pathInTemp("secret.txt"); @@ -261,19 +265,72 @@ public class CryptoToolsTest { assertEquals(greatSecret, Files.readString(decryptedPath)); } + @Test + void can_end_to_end_keygen_encrypt_and_decrypt_via_stdio_streams() throws IOException { + String greatSecret = "forbidden knowledge about cats.txt"; + + var privPath = pathInTemp("priv.txt"); + var pubPath = pathInTemp("pub.txt"); + var procOut = runMain(List.of( + "keygen", + "--private-out-file", absPathOf(privPath), + "--public-out-file", absPathOf(pubPath))); + assertEquals(0, procOut.exitCode()); + assertEquals("", procOut.stdOut()); + assertEquals("", procOut.stdErr()); + + assertTrue(privPath.toFile().exists()); + assertTrue(pubPath.toFile().exists()); + + var encryptedPath = pathInTemp("encrypted.bin"); + // Encryption emits token on stdout, so can't support ciphertext output via that channel. + procOut = runMain(List.of( + "encrypt", + "-", // Encrypt stdin + "--output-file", absPathOf(encryptedPath), + "--recipient-public-key", Files.readString(pubPath), + "--key-id", "1234"), + toUtf8Bytes(greatSecret)); + assertEquals(0, procOut.exitCode()); + assertEquals("", procOut.stdErr()); + + var token = procOut.stdOut(); + assertFalse(token.isBlank()); + + assertTrue(encryptedPath.toFile().exists()); + + procOut = runMain(List.of( + "decrypt", + "-", // Decrypt stdin + "--output-file", "-", // Plaintext to stdout + "--recipient-private-key-file", absPathOf(privPath), + "--key-id", "1234", + "--token", token + ), Files.readAllBytes(encryptedPath)); + + assertEquals(0, procOut.exitCode()); + assertEquals("", procOut.stdErr()); + assertEquals(greatSecret, procOut.stdOut()); + } + private ProcessOutput runMain(List<String> args) { + return runMain(args, EMPTY_BYTES); + } + + private ProcessOutput runMain(List<String> args, byte[] stdInBytes) { // Expect that this is used for running a command that is not supposed to fail. But if it does, // include exception trace in stderr to make it easier to debug. - return runMain(args, Map.of("VESPA_DEBUG", "true")); + return runMain(args, stdInBytes, Map.of("VESPA_DEBUG", "true")); } - private ProcessOutput runMain(List<String> args, Map<String, String> env) { + private ProcessOutput runMain(List<String> args, byte[] stdInBytes, Map<String, String> env) { var stdOutBytes = new ByteArrayOutputStream(); var stdErrBytes = new ByteArrayOutputStream(); + var stdIn = new ByteArrayInputStream(stdInBytes); var stdOut = new PrintStream(stdOutBytes); var stdError = new PrintStream(stdErrBytes); - int exitCode = new Main(stdOut, stdError).execute(args.toArray(new String[0]), env); + int exitCode = new Main(stdIn, stdOut, stdError).execute(args.toArray(new String[0]), env); stdOut.flush(); stdError.flush(); diff --git a/vespaclient-java/src/test/resources/expected-decrypt-help-output.txt b/vespaclient-java/src/test/resources/expected-decrypt-help-output.txt index 979133a562b..ef59741cd30 100644 --- a/vespaclient-java/src/test/resources/expected-decrypt-help-output.txt +++ b/vespaclient-java/src/test/resources/expected-decrypt-help-output.txt @@ -2,6 +2,9 @@ usage: vespa-security decrypt <encrypted file> <options> Decrypts a file using a provided token and a secret private key. The file must previously have been encrypted using the public key component of the given private key. + +To decrypt the contents of STDIN, specify an input file of '-' (without +the quotes). -h,--help Show help -i,--key-id <arg> Numeric ID of recipient key. If this is not provided, the key ID @@ -9,7 +12,9 @@ given private key. not verified. -k,--recipient-private-key-file <arg> Recipient private key file -o,--output-file <arg> Output file for decrypted - plaintext + plaintext. Specify '-' (without + the quotes) to write plaintext to + STDOUT instead of a file. -t,--token <arg> Token generated when the input file was encrypted Note: this is a BETA tool version; its interface may be changed at any diff --git a/vespaclient-java/src/test/resources/expected-encrypt-help-output.txt b/vespaclient-java/src/test/resources/expected-encrypt-help-output.txt index 848035f417e..5e1da32cbe7 100644 --- a/vespaclient-java/src/test/resources/expected-encrypt-help-output.txt +++ b/vespaclient-java/src/test/resources/expected-encrypt-help-output.txt @@ -3,6 +3,9 @@ One-way encrypts a file using the public key of a recipient. A public token is printed on standard out. The recipient can use this token to decrypt the file using their private key. The token does not have to be kept secret. + +To encrypt the contents of STDIN, specify an input file of '-' (without +the quotes). -h,--help Show help -i,--key-id <arg> Numeric ID of recipient key -o,--output-file <arg> Output file (will be truncated if it |