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/java | |
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/java')
-rw-r--r-- | vespaclient-java/src/test/java/com/yahoo/vespa/security/tool/CryptoToolsTest.java | 73 |
1 files changed, 65 insertions, 8 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(); |