mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-22 20:32:05 +01:00
Rework CLI tests
This commit is contained in:
parent
fd55ce3657
commit
858f8e00f3
11 changed files with 1548 additions and 473 deletions
|
@ -5,87 +5,96 @@
|
||||||
package org.pgpainless.cli.commands;
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
public class ArmorCmdTest {
|
public class ArmorCmdTest extends CLITest {
|
||||||
|
|
||||||
private static PrintStream originalSout;
|
public ArmorCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(ArmorCmdTest.class));
|
||||||
@BeforeEach
|
|
||||||
public void saveSout() {
|
|
||||||
originalSout = System.out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
private static final String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
public void restoreSout() {
|
"Version: PGPainless\n" +
|
||||||
System.setOut(originalSout);
|
"Comment: 62E9 DDA4 F20F 8341 D2BC 4B4C 8B07 5177 01F9 534C\n" +
|
||||||
}
|
"Comment: alice@pgpainless.org\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEY2vOkhYJKwYBBAHaRw8BAQdAqGOtLd1tKnuwaYYcdr2/7C0cPiCCggRMKG+W\n" +
|
||||||
|
"t32QQdEAAP9VaBzjk/AaAqyykZnQHmS1HByEvRLv5/4yJMSr22451BFjtBRhbGlj\n" +
|
||||||
|
"ZUBwZ3BhaW5sZXNzLm9yZ4iOBBMWCgBBBQJja86SCRCLB1F3AflTTBYhBGLp3aTy\n" +
|
||||||
|
"D4NB0rxLTIsHUXcB+VNMAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAACZhAP4s\n" +
|
||||||
|
"8hn/RBDvyLvGROOd15EYATnWlgyi+b5WXP6cELalJwD1FZy3RROhfNtZWcJPS43f\n" +
|
||||||
|
"G03pYNyb0NXoitIMAaXEB5xdBGNrzpISCisGAQQBl1UBBQEBB0CqCcYethOynfni\n" +
|
||||||
|
"8uRO+r/cZWp9hCLy8pRIExKqzcyEFAMBCAcAAP9sRRLoZkLpDaTNNrtIBovXu2AN\n" +
|
||||||
|
"hL8keUMWtVcuEHnkQA6iiHUEGBYKAB0FAmNrzpICngECmwwFFgIDAQAECwkIBwUV\n" +
|
||||||
|
"CgkICwAKCRCLB1F3AflTTBVpAP491etrjqCMWx2bBaw3K1vP0Mix6U0vF3J4kP9U\n" +
|
||||||
|
"eZm6owEA4kX9VAGESvLgIc7CEiswmxdWjxnLQyCRtWXfjgFmYQucWARja86SFgkr\n" +
|
||||||
|
"BgEEAdpHDwEBB0DBslhDpWC6CV3xJUSo071NSO5Cf4fgOwOj+QHs8mpFbwABAPkQ\n" +
|
||||||
|
"ioSydYiMi04LyfPohyrhhcdJDHallQg+jYHHUb2pEJCI1QQYFgoAfQUCY2vOkgKe\n" +
|
||||||
|
"AQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNrzpIACgkQiHlkvEXh+f1e\n" +
|
||||||
|
"ywEA9A2GLU9LxCJxZf2X4qcZY//YJDChIZHPnY0Vaek1DsMBAN1YILrH2rxQeCXj\n" +
|
||||||
|
"m4bUKfJIRrGt6ZJscwORgNI1dFQFAAoJEIsHUXcB+VNMK3gA/3vvPm57JsHA860w\n" +
|
||||||
|
"lB4D1II71oFNL8TFnJqTAvpSKe1AAP49S4mKB4PE0ElcDo7n+nEYt6ba8IMRDlMo\n" +
|
||||||
|
"rsH85mUgCw==\n" +
|
||||||
|
"=EMKf\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void armorSecretKey() throws IOException {
|
||||||
public void armorSecretKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
byte[] binary = secretKeys.getEncoded();
|
||||||
.modernKeyRing("alice@pgpainless.org");
|
|
||||||
byte[] bytes = secretKey.getEncoded();
|
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(bytes));
|
pipeBytesToStdin(binary);
|
||||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(armorOut));
|
assertSuccess(executeCommand("armor"));
|
||||||
PGPainlessCLI.execute("armor");
|
|
||||||
|
|
||||||
PGPSecretKeyRing armored = PGPainless.readKeyRing().secretKeyRing(armorOut.toString());
|
PGPSecretKeyRing armored = PGPainless.readKeyRing().secretKeyRing(armorOut.toString());
|
||||||
assertArrayEquals(secretKey.getEncoded(), armored.getEncoded());
|
assertArrayEquals(secretKeys.getEncoded(), armored.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void armorPublicKey() throws IOException {
|
||||||
public void armorPublicKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
||||||
.modernKeyRing("alice@pgpainless.org");
|
|
||||||
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
PGPPublicKeyRing publicKey = PGPainless.extractCertificate(secretKey);
|
||||||
byte[] bytes = publicKey.getEncoded();
|
byte[] bytes = publicKey.getEncoded();
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(bytes));
|
pipeBytesToStdin(bytes);
|
||||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(armorOut));
|
assertSuccess(executeCommand("armor"));
|
||||||
PGPainlessCLI.execute("armor");
|
|
||||||
|
|
||||||
PGPPublicKeyRing armored = PGPainless.readKeyRing().publicKeyRing(armorOut.toString());
|
PGPPublicKeyRing armored = PGPainless.readKeyRing().publicKeyRing(armorOut.toString());
|
||||||
assertArrayEquals(publicKey.getEncoded(), armored.getEncoded());
|
assertArrayEquals(publicKey.getEncoded(), armored.getEncoded());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void armorMessage() throws IOException {
|
||||||
public void armorMessage() {
|
|
||||||
String message = "Hello, World!\n";
|
String message = "Hello, World!\n";
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)));
|
pipeStringToStdin(message);
|
||||||
ByteArrayOutputStream armorOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream armorOut = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(armorOut));
|
assertSuccess(executeCommand("armor"));
|
||||||
PGPainlessCLI.execute("armor");
|
|
||||||
|
|
||||||
String armored = armorOut.toString();
|
String armored = armorOut.toString();
|
||||||
|
|
||||||
assertTrue(armored.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
assertTrue(armored.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
||||||
assertTrue(armored.contains("SGVsbG8sIFdvcmxkIQo="));
|
assertTrue(armored.contains("SGVsbG8sIFdvcmxkIQo="));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void labelNotYetSupported() throws IOException {
|
||||||
|
pipeStringToStdin("Hello, World!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("armor", "--label", "Message");
|
||||||
|
assertEquals(SOPGPException.UnsupportedOption.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,166 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileInputStream;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.PrintStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
|
import org.bouncycastle.util.io.Streams;
|
||||||
|
import org.junit.jupiter.api.AfterEach;
|
||||||
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
|
import org.opentest4j.TestAbortedException;
|
||||||
|
import org.pgpainless.cli.TestUtils;
|
||||||
|
import org.pgpainless.sop.SOPImpl;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import sop.cli.picocli.SopCLI;
|
||||||
|
|
||||||
|
public abstract class CLITest {
|
||||||
|
|
||||||
|
protected File testDirectory;
|
||||||
|
protected InputStream stdin;
|
||||||
|
protected PrintStream stdout;
|
||||||
|
|
||||||
|
protected final Logger LOGGER;
|
||||||
|
|
||||||
|
|
||||||
|
public CLITest(@Nonnull Logger logger) {
|
||||||
|
LOGGER = logger;
|
||||||
|
SopCLI.setSopInstance(new SOPImpl());
|
||||||
|
}
|
||||||
|
|
||||||
|
@BeforeEach
|
||||||
|
public void setup() throws IOException {
|
||||||
|
testDirectory = TestUtils.createTempDirectory();
|
||||||
|
testDirectory.deleteOnExit();
|
||||||
|
LOGGER.debug(testDirectory.getAbsolutePath());
|
||||||
|
stdin = System.in;
|
||||||
|
stdout = System.out;
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterEach
|
||||||
|
public void cleanup() throws IOException {
|
||||||
|
resetStreams();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File nonExistentFile(String name) {
|
||||||
|
File file = new File(testDirectory, name);
|
||||||
|
if (file.exists()) {
|
||||||
|
throw new TestAbortedException("File " + file.getAbsolutePath() + " already exists.");
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public File pipeStdoutToFile(String name) throws IOException {
|
||||||
|
File file = new File(testDirectory, name);
|
||||||
|
file.deleteOnExit();
|
||||||
|
if (!file.createNewFile()) {
|
||||||
|
throw new TestAbortedException("Cannot create new file " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
System.setOut(new PrintStream(Files.newOutputStream(file.toPath())));
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ByteArrayOutputStream pipeStdoutToStream() {
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
pipeStdoutToStream(out);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pipeStdoutToStream(OutputStream stream) {
|
||||||
|
System.setOut(new PrintStream(stream));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pipeFileToStdin(File file) throws IOException {
|
||||||
|
System.setIn(Files.newInputStream(file.toPath()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pipeBytesToStdin(byte[] bytes) {
|
||||||
|
System.setIn(new ByteArrayInputStream(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void pipeStringToStdin(String string) {
|
||||||
|
System.setIn(new ByteArrayInputStream(string.getBytes(StandardCharsets.UTF_8)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetStdout() {
|
||||||
|
if (System.out != stdout) {
|
||||||
|
System.out.flush();
|
||||||
|
System.out.close();
|
||||||
|
}
|
||||||
|
System.setOut(stdout);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetStdin() throws IOException {
|
||||||
|
if (System.in != stdin) {
|
||||||
|
System.in.close();
|
||||||
|
}
|
||||||
|
System.setIn(stdin);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetStreams() throws IOException {
|
||||||
|
resetStdout();
|
||||||
|
resetStdin();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File writeFile(String name, String data) throws IOException {
|
||||||
|
return writeFile(name, data.getBytes(StandardCharsets.UTF_8));
|
||||||
|
}
|
||||||
|
|
||||||
|
public File writeFile(String name, byte[] bytes) throws IOException {
|
||||||
|
return writeFile(name, new ByteArrayInputStream(bytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
public File writeFile(String name, InputStream data) throws IOException {
|
||||||
|
File file = new File(testDirectory, name);
|
||||||
|
if (!file.createNewFile()) {
|
||||||
|
throw new TestAbortedException("Cannot create new file " + file.getAbsolutePath());
|
||||||
|
}
|
||||||
|
file.deleteOnExit();
|
||||||
|
try (FileOutputStream fileOut = new FileOutputStream(file)) {
|
||||||
|
Streams.pipeAll(data, fileOut);
|
||||||
|
fileOut.flush();
|
||||||
|
}
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] readBytesFromFile(File file) {
|
||||||
|
ByteArrayOutputStream buffer = new ByteArrayOutputStream();
|
||||||
|
try (FileInputStream fileIn = new FileInputStream(file)) {
|
||||||
|
Streams.pipeAll(fileIn, buffer);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
throw new TestAbortedException("File " + file.getAbsolutePath() + " does not exist!", e);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new TestAbortedException("Cannot read from file " + file.getAbsolutePath(), e);
|
||||||
|
}
|
||||||
|
return buffer.toByteArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String readStringFromFile(File file) {
|
||||||
|
return new String(readBytesFromFile(file), StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
public int executeCommand(String... command) throws IOException {
|
||||||
|
int exitCode = SopCLI.execute(command);
|
||||||
|
resetStreams();
|
||||||
|
return exitCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void assertSuccess(int exitCode) {
|
||||||
|
assertEquals(0, exitCode, "Expected successful program execution");
|
||||||
|
}
|
||||||
|
}
|
|
@ -7,73 +7,73 @@ package org.pgpainless.cli.commands;
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class DearmorCmdTest {
|
public class DearmorCmdTest extends CLITest {
|
||||||
|
|
||||||
private PrintStream originalSout;
|
public DearmorCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(DearmorCmdTest.class));
|
||||||
@BeforeEach
|
|
||||||
public void saveSout() {
|
|
||||||
this.originalSout = System.out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
private static final String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
public void restoreSout() {
|
"Version: PGPainless\n" +
|
||||||
System.setOut(originalSout);
|
"Comment: 62E9 DDA4 F20F 8341 D2BC 4B4C 8B07 5177 01F9 534C\n" +
|
||||||
}
|
"Comment: alice@pgpainless.org\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEY2vOkhYJKwYBBAHaRw8BAQdAqGOtLd1tKnuwaYYcdr2/7C0cPiCCggRMKG+W\n" +
|
||||||
|
"t32QQdEAAP9VaBzjk/AaAqyykZnQHmS1HByEvRLv5/4yJMSr22451BFjtBRhbGlj\n" +
|
||||||
|
"ZUBwZ3BhaW5sZXNzLm9yZ4iOBBMWCgBBBQJja86SCRCLB1F3AflTTBYhBGLp3aTy\n" +
|
||||||
|
"D4NB0rxLTIsHUXcB+VNMAp4BApsBBRYCAwEABAsJCAcFFQoJCAsCmQEAACZhAP4s\n" +
|
||||||
|
"8hn/RBDvyLvGROOd15EYATnWlgyi+b5WXP6cELalJwD1FZy3RROhfNtZWcJPS43f\n" +
|
||||||
|
"G03pYNyb0NXoitIMAaXEB5xdBGNrzpISCisGAQQBl1UBBQEBB0CqCcYethOynfni\n" +
|
||||||
|
"8uRO+r/cZWp9hCLy8pRIExKqzcyEFAMBCAcAAP9sRRLoZkLpDaTNNrtIBovXu2AN\n" +
|
||||||
|
"hL8keUMWtVcuEHnkQA6iiHUEGBYKAB0FAmNrzpICngECmwwFFgIDAQAECwkIBwUV\n" +
|
||||||
|
"CgkICwAKCRCLB1F3AflTTBVpAP491etrjqCMWx2bBaw3K1vP0Mix6U0vF3J4kP9U\n" +
|
||||||
|
"eZm6owEA4kX9VAGESvLgIc7CEiswmxdWjxnLQyCRtWXfjgFmYQucWARja86SFgkr\n" +
|
||||||
|
"BgEEAdpHDwEBB0DBslhDpWC6CV3xJUSo071NSO5Cf4fgOwOj+QHs8mpFbwABAPkQ\n" +
|
||||||
|
"ioSydYiMi04LyfPohyrhhcdJDHallQg+jYHHUb2pEJCI1QQYFgoAfQUCY2vOkgKe\n" +
|
||||||
|
"AQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNrzpIACgkQiHlkvEXh+f1e\n" +
|
||||||
|
"ywEA9A2GLU9LxCJxZf2X4qcZY//YJDChIZHPnY0Vaek1DsMBAN1YILrH2rxQeCXj\n" +
|
||||||
|
"m4bUKfJIRrGt6ZJscwORgNI1dFQFAAoJEIsHUXcB+VNMK3gA/3vvPm57JsHA860w\n" +
|
||||||
|
"lB4D1II71oFNL8TFnJqTAvpSKe1AAP49S4mKB4PE0ElcDo7n+nEYt6ba8IMRDlMo\n" +
|
||||||
|
"rsH85mUgCw==\n" +
|
||||||
|
"=EMKf\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----\n";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void dearmorSecretKey() throws IOException {
|
||||||
public void dearmorSecretKey() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
||||||
.modernKeyRing("alice@pgpainless.org");
|
|
||||||
String armored = PGPainless.asciiArmor(secretKey);
|
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
pipeStringToStdin(key);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream dearmored = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(out));
|
assertSuccess(executeCommand("dearmor"));
|
||||||
PGPainlessCLI.execute("dearmor");
|
|
||||||
|
|
||||||
assertArrayEquals(secretKey.getEncoded(), out.toByteArray());
|
assertArrayEquals(secretKey.getEncoded(), dearmored.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void dearmorCertificate() throws IOException {
|
||||||
public void dearmorCertificate() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
PGPSecretKeyRing secretKey = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
PGPSecretKeyRing secretKey = PGPainless.generateKeyRing()
|
|
||||||
.modernKeyRing("alice@pgpainless.org");
|
|
||||||
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
PGPPublicKeyRing certificate = PGPainless.extractCertificate(secretKey);
|
||||||
String armored = PGPainless.asciiArmor(certificate);
|
String armoredCert = PGPainless.asciiArmor(certificate);
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
pipeStringToStdin(armoredCert);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(out));
|
assertSuccess(executeCommand("dearmor"));
|
||||||
PGPainlessCLI.execute("dearmor");
|
|
||||||
|
|
||||||
assertArrayEquals(certificate.getEncoded(), out.toByteArray());
|
assertArrayEquals(certificate.getEncoded(), out.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void dearmorMessage() throws IOException {
|
||||||
public void dearmorMessage() {
|
|
||||||
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
String armored = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
"Version: BCPG v1.69\n" +
|
"Version: BCPG v1.69\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -81,10 +81,9 @@ public class DearmorCmdTest {
|
||||||
"=fkLo\n" +
|
"=fkLo\n" +
|
||||||
"-----END PGP MESSAGE-----";
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
System.setIn(new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)));
|
pipeStringToStdin(armored);
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(out));
|
assertSuccess(executeCommand("dearmor"));
|
||||||
PGPainlessCLI.execute("dearmor");
|
|
||||||
|
|
||||||
assertEquals("Hello, World\n", out.toString());
|
assertEquals("Hello, World\n", out.toString());
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,42 +4,96 @@
|
||||||
|
|
||||||
package org.pgpainless.cli.commands;
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
public class ExtractCertCmdTest {
|
public class ExtractCertCmdTest extends CLITest {
|
||||||
|
|
||||||
|
public ExtractCertCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(ExtractCertCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
|
||||||
public void testExtractCert() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
public void testExtractCert() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, PGPException, IOException {
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing()
|
||||||
.simpleEcKeyRing("Juliet Capulet <juliet@capulet.lit>");
|
.simpleEcKeyRing("Juliet Capulet <juliet@capulet.lit>");
|
||||||
ByteArrayInputStream inputStream = new ByteArrayInputStream(secretKeys.getEncoded());
|
|
||||||
System.setIn(inputStream);
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
pipeBytesToStdin(secretKeys.getEncoded());
|
||||||
System.setOut(new PrintStream(out));
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("extract-cert", "--armor"));
|
||||||
|
|
||||||
|
assertTrue(out.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||||
|
|
||||||
PGPainlessCLI.execute("extract-cert");
|
|
||||||
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(out.toByteArray());
|
PGPPublicKeyRing publicKeys = PGPainless.readKeyRing().publicKeyRing(out.toByteArray());
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
|
KeyRingInfo info = PGPainless.inspectKeyRing(publicKeys);
|
||||||
assertFalse(info.isSecretKey());
|
assertFalse(info.isSecretKey());
|
||||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtractCertFromCertFails() throws IOException {
|
||||||
|
// Generate key
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||||
|
|
||||||
|
// extract cert from key (success)
|
||||||
|
pipeFileToStdin(keyFile);
|
||||||
|
File certFile = pipeStdoutToFile("cert.asc");
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
|
// extract cert from cert (fail)
|
||||||
|
pipeFileToStdin(certFile);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("extract-cert");
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void extractCertFromGarbageFails() throws IOException {
|
||||||
|
pipeStringToStdin("This is a bunch of garbage!");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("extract-cert");
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtractCertUnarmored() throws IOException {
|
||||||
|
// Generate key
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||||
|
|
||||||
|
// extract cert from key (success)
|
||||||
|
pipeFileToStdin(keyFile);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("extract-cert", "--no-armor"));
|
||||||
|
|
||||||
|
assertFalse(out.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||||
|
|
||||||
|
pipeBytesToStdin(out.toByteArray());
|
||||||
|
ByteArrayOutputStream armored = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("armor"));
|
||||||
|
|
||||||
|
assertTrue(armored.toString().startsWith("-----BEGIN PGP PUBLIC KEY BLOCK-----\n"));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,106 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.cli.commands;
|
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
||||||
import static org.pgpainless.cli.TestUtils.ARMOR_PRIVATE_KEY_HEADER_BYTES;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.Arrays;
|
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKey;
|
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
|
||||||
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor;
|
|
||||||
import org.junit.jupiter.api.Test;
|
|
||||||
import org.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
|
||||||
import org.pgpainless.cli.TestUtils;
|
|
||||||
import org.pgpainless.key.info.KeyInfo;
|
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
|
||||||
import org.pgpainless.key.protection.UnlockSecretKey;
|
|
||||||
import org.pgpainless.util.Passphrase;
|
|
||||||
|
|
||||||
public class GenerateCertCmdTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@FailOnSystemExit
|
|
||||||
public void testKeyGeneration() throws IOException, PGPException {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
System.setOut(new PrintStream(out));
|
|
||||||
PGPainlessCLI.execute("generate-key", "--armor", "Juliet Capulet <juliet@capulet.lit>");
|
|
||||||
|
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(out.toByteArray());
|
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
|
||||||
assertTrue(info.isFullyDecrypted());
|
|
||||||
assertTrue(info.isUserIdValid("Juliet Capulet <juliet@capulet.lit>"));
|
|
||||||
|
|
||||||
for (PGPSecretKey key : secretKeys) {
|
|
||||||
assertTrue(testPassphrase(key, null));
|
|
||||||
}
|
|
||||||
|
|
||||||
byte[] outBegin = new byte[37];
|
|
||||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
|
||||||
assertArrayEquals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@FailOnSystemExit
|
|
||||||
public void testGenerateKeyWithPassword() throws IOException, PGPException {
|
|
||||||
PrintStream orig = System.out;
|
|
||||||
try {
|
|
||||||
// Write password to file
|
|
||||||
File tempDir = TestUtils.createTempDirectory();
|
|
||||||
File passwordFile = TestUtils.writeTempFile(tempDir, "sw0rdf1sh".getBytes(StandardCharsets.UTF_8));
|
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
System.setOut(new PrintStream(out));
|
|
||||||
PGPainlessCLI.execute("generate-key", "Juliet Capulet <juliet@capulet.lit>",
|
|
||||||
"--with-key-password", passwordFile.getAbsolutePath());
|
|
||||||
|
|
||||||
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(out.toByteArray());
|
|
||||||
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
|
||||||
assertFalse(info.isFullyDecrypted());
|
|
||||||
assertTrue(info.isFullyEncrypted());
|
|
||||||
|
|
||||||
for (PGPSecretKey key : secretKeys) {
|
|
||||||
assertTrue(testPassphrase(key, "sw0rdf1sh"));
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
System.setOut(orig);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean testPassphrase(PGPSecretKey key, String passphrase) throws PGPException {
|
|
||||||
if (KeyInfo.isEncrypted(key)) {
|
|
||||||
UnlockSecretKey.unlockSecretKey(key, Passphrase.fromPassword(passphrase));
|
|
||||||
} else {
|
|
||||||
if (passphrase != null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
UnlockSecretKey.unlockSecretKey(key, (PBESecretKeyDecryptor) null);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
@FailOnSystemExit
|
|
||||||
public void testNoArmor() {
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
|
||||||
System.setOut(new PrintStream(out));
|
|
||||||
PGPainlessCLI.execute("generate-key", "--no-armor", "Test <test@test.test>");
|
|
||||||
|
|
||||||
byte[] outBegin = new byte[37];
|
|
||||||
System.arraycopy(out.toByteArray(), 0, outBegin, 0, 37);
|
|
||||||
assertFalse(Arrays.equals(outBegin, ARMOR_PRIVATE_KEY_HEADER_BYTES));
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,96 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
|
import org.bouncycastle.util.encoders.Hex;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.pgpainless.PGPainless;
|
||||||
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
|
import org.pgpainless.key.protection.UnlockSecretKey;
|
||||||
|
import org.pgpainless.util.Passphrase;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
|
public class GenerateKeyCmdTest extends CLITest {
|
||||||
|
|
||||||
|
public GenerateKeyCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(GenerateKeyCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateKey() throws IOException {
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||||
|
|
||||||
|
String key = readStringFromFile(keyFile);
|
||||||
|
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertTrue(info.isFullyDecrypted());
|
||||||
|
assertEquals(Collections.singletonList("Alice <alice@pgpainless.org>"), info.getUserIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateBinaryKey() throws IOException {
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("generate-key", "--no-armor",
|
||||||
|
"Alice <alice@pgpainless.org>"));
|
||||||
|
|
||||||
|
byte[] key = out.toByteArray();
|
||||||
|
String firstHexOctet = Hex.toHexString(key, 0, 1);
|
||||||
|
assertTrue(firstHexOctet.equals("c5") || firstHexOctet.equals("94"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGenerateKeyWithMultipleUserIds() throws IOException {
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("generate-key",
|
||||||
|
"Alice <alice@pgpainless.org>", "Alice <alice@openpgp.org>"));
|
||||||
|
|
||||||
|
String key = out.toString();
|
||||||
|
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertTrue(info.isFullyDecrypted());
|
||||||
|
assertEquals(Arrays.asList("Alice <alice@pgpainless.org>", "Alice <alice@openpgp.org>"), info.getUserIds());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testPasswordProtectedKey() throws IOException, PGPException {
|
||||||
|
File passwordFile = writeFile("password", "sw0rdf1sh");
|
||||||
|
passwordFile.deleteOnExit();
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("generate-key",
|
||||||
|
"--with-key-password", passwordFile.getAbsolutePath(), "Alice <alice@pgpainless.org>"));
|
||||||
|
|
||||||
|
String key = out.toString();
|
||||||
|
assertTrue(key.startsWith("-----BEGIN PGP PRIVATE KEY BLOCK-----\n"));
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(key);
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(secretKeys);
|
||||||
|
assertTrue(info.isFullyEncrypted());
|
||||||
|
|
||||||
|
assertNotNull(UnlockSecretKey.unlockSecretKey(secretKeys.getSecretKey(), Passphrase.fromPassword("sw0rdf1sh")));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGeneratePasswordProtectedKey_missingPasswordFile() throws IOException {
|
||||||
|
int exit = executeCommand("generate-key",
|
||||||
|
"--with-key-password", "nonexistent", "Alice <alice@pgpainless.org>");
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.MissingInput.EXIT_CODE, exit, "Expected MISSING_INPUT (" + SOPGPException.MissingInput.EXIT_CODE + ")");
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,33 +6,18 @@ package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
import static org.junit.jupiter.api.Assertions.fail;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.ExpectSystemExitWithStatus;
|
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.junit.jupiter.api.AfterEach;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
|
||||||
import org.pgpainless.cli.TestUtils;
|
import org.pgpainless.cli.TestUtils;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import sop.exception.SOPGPException;
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
public class InlineDetachCmdTest {
|
public class InlineDetachCmdTest extends CLITest {
|
||||||
|
|
||||||
private PrintStream originalSout;
|
|
||||||
private static File tempDir;
|
|
||||||
private static File certFile;
|
|
||||||
|
|
||||||
private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
"Version: BCPG v1.64\n" +
|
"Version: BCPG v1.64\n" +
|
||||||
|
@ -49,28 +34,6 @@ public class InlineDetachCmdTest {
|
||||||
"=a1W7\n" +
|
"=a1W7\n" +
|
||||||
"-----END PGP PUBLIC KEY BLOCK-----";
|
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void createTempDir() throws IOException {
|
|
||||||
tempDir = TestUtils.createTempDirectory();
|
|
||||||
|
|
||||||
certFile = new File(tempDir, "cert.asc");
|
|
||||||
assertTrue(certFile.createNewFile());
|
|
||||||
try (FileOutputStream out = new FileOutputStream(certFile)) {
|
|
||||||
ByteArrayInputStream in = new ByteArrayInputStream(CERT.getBytes(StandardCharsets.UTF_8));
|
|
||||||
Streams.pipeAll(in, out);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@BeforeEach
|
|
||||||
public void saveSout() {
|
|
||||||
this.originalSout = System.out;
|
|
||||||
}
|
|
||||||
|
|
||||||
@AfterEach
|
|
||||||
public void restoreSout() {
|
|
||||||
System.setOut(originalSout);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static final String CLEAR_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
private static final String CLEAR_SIGNED_MESSAGE = "-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||||
"Hash: SHA512\n" +
|
"Hash: SHA512\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
|
@ -103,91 +66,67 @@ public class InlineDetachCmdTest {
|
||||||
"Unfold the imagined happiness that both\n" +
|
"Unfold the imagined happiness that both\n" +
|
||||||
"Receive in either by this dear encounter.";
|
"Receive in either by this dear encounter.";
|
||||||
|
|
||||||
|
public InlineDetachCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(InlineDetachCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void detachInbandSignatureAndMessage() throws IOException {
|
public void detachInbandSignatureAndMessage() throws IOException {
|
||||||
// Clearsigned In
|
pipeStringToStdin(CLEAR_SIGNED_MESSAGE);
|
||||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
ByteArrayOutputStream msgOut = pipeStdoutToStream();
|
||||||
System.setIn(clearSignedIn);
|
File sigFile = nonExistentFile("sig.out");
|
||||||
|
|
||||||
// Plaintext Out
|
assertSuccess(executeCommand("inline-detach", "--signatures-out", sigFile.getAbsolutePath()));
|
||||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
assertTrue(sigFile.exists(), "Signature file must have been written.");
|
||||||
System.setOut(new PrintStream(msgOut));
|
|
||||||
|
|
||||||
// Detach
|
|
||||||
File tempSigFile = new File(tempDir, "sig.out");
|
|
||||||
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + tempSigFile.getAbsolutePath()});
|
|
||||||
|
|
||||||
// Test equality with expected values
|
// Test equality with expected values
|
||||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||||
try (FileInputStream sigIn = new FileInputStream(tempSigFile)) {
|
String sig = readStringFromFile(sigFile);
|
||||||
ByteArrayOutputStream sigBytes = new ByteArrayOutputStream();
|
TestUtils.assertSignatureIsArmored(sig.getBytes());
|
||||||
Streams.pipeAll(sigIn, sigBytes);
|
|
||||||
String sig = sigBytes.toString();
|
|
||||||
TestUtils.assertSignatureIsArmored(sigBytes.toByteArray());
|
|
||||||
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE, sig);
|
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE, sig);
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
fail("Signature File must have been written.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if produced signature still checks out
|
// Check if produced signature still checks out
|
||||||
System.setIn(new ByteArrayInputStream(msgOut.toByteArray()));
|
File certFile = writeFile("cert.asc", CERT);
|
||||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
pipeStringToStdin(msgOut.toString());
|
||||||
System.setOut(new PrintStream(verifyOut));
|
ByteArrayOutputStream verifyOut = pipeStdoutToStream();
|
||||||
PGPainlessCLI.main(new String[] {"verify", tempSigFile.getAbsolutePath(), certFile.getAbsolutePath()});
|
assertSuccess(executeCommand("verify", sigFile.getAbsolutePath(), certFile.getAbsolutePath()));
|
||||||
|
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n",
|
||||||
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n", verifyOut.toString());
|
verifyOut.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void detachInbandSignatureAndMessageNoArmor() throws IOException {
|
public void detachInbandSignatureAndMessageNoArmor() throws IOException {
|
||||||
// Clearsigned In
|
pipeStringToStdin(CLEAR_SIGNED_MESSAGE);
|
||||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
ByteArrayOutputStream msgOut = pipeStdoutToStream();
|
||||||
System.setIn(clearSignedIn);
|
File sigFile = nonExistentFile("sig.out");
|
||||||
|
|
||||||
// Plaintext Out
|
assertSuccess(executeCommand("inline-detach", "--signatures-out", sigFile.getAbsolutePath(), "--no-armor"));
|
||||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
|
||||||
System.setOut(new PrintStream(msgOut));
|
|
||||||
|
|
||||||
// Detach
|
|
||||||
File tempSigFile = new File(tempDir, "sig.asc");
|
|
||||||
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + tempSigFile.getAbsolutePath(), "--no-armor"});
|
|
||||||
|
|
||||||
// Test equality with expected values
|
// Test equality with expected values
|
||||||
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
assertEquals(CLEAR_SIGNED_BODY, msgOut.toString());
|
||||||
try (FileInputStream sigIn = new FileInputStream(tempSigFile)) {
|
assertTrue(sigFile.exists(), "Signature file must have been written.");
|
||||||
ByteArrayOutputStream sigBytes = new ByteArrayOutputStream();
|
byte[] sig = readBytesFromFile(sigFile);
|
||||||
Streams.pipeAll(sigIn, sigBytes);
|
|
||||||
byte[] sig = sigBytes.toByteArray();
|
|
||||||
TestUtils.assertSignatureIsNotArmored(sig);
|
TestUtils.assertSignatureIsNotArmored(sig);
|
||||||
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE.getBytes(StandardCharsets.UTF_8), sig);
|
TestUtils.assertSignatureEquals(CLEAR_SIGNED_SIGNATURE.getBytes(StandardCharsets.UTF_8), sig);
|
||||||
} catch (FileNotFoundException e) {
|
|
||||||
fail("Signature File must have been written.", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if produced signature still checks out
|
// Check if produced signature still checks out
|
||||||
System.setIn(new ByteArrayInputStream(msgOut.toByteArray()));
|
pipeBytesToStdin(msgOut.toByteArray());
|
||||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
ByteArrayOutputStream verifyOut = pipeStdoutToStream();
|
||||||
System.setOut(new PrintStream(verifyOut));
|
File certFile = writeFile("cert.asc", CERT);
|
||||||
PGPainlessCLI.main(new String[] {"verify", tempSigFile.getAbsolutePath(), certFile.getAbsolutePath()});
|
assertSuccess(executeCommand("verify", sigFile.getAbsolutePath(), certFile.getAbsolutePath()));
|
||||||
|
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n",
|
||||||
assertEquals("2021-05-15T16:08:06Z 4F665C4DC2C4660BC6425E415736E6931ACF370C 4F665C4DC2C4660BC6425E415736E6931ACF370C\n", verifyOut.toString());
|
verifyOut.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ExpectSystemExitWithStatus(SOPGPException.OutputExists.EXIT_CODE)
|
|
||||||
public void existingSignatureOutCausesException() throws IOException {
|
public void existingSignatureOutCausesException() throws IOException {
|
||||||
// Clearsigned In
|
pipeStringToStdin(CLEAR_SIGNED_MESSAGE);
|
||||||
ByteArrayInputStream clearSignedIn = new ByteArrayInputStream(CLEAR_SIGNED_MESSAGE.getBytes(StandardCharsets.UTF_8));
|
ByteArrayOutputStream msgOut = pipeStdoutToStream();
|
||||||
System.setIn(clearSignedIn);
|
File existingSigFile = writeFile("sig.asc", CLEAR_SIGNED_SIGNATURE);
|
||||||
|
int exit = executeCommand("inline-detach", "--signatures-out", existingSigFile.getAbsolutePath());
|
||||||
// Plaintext Out
|
assertEquals(SOPGPException.OutputExists.EXIT_CODE, exit);
|
||||||
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
|
assertEquals(0, msgOut.size());
|
||||||
System.setOut(new PrintStream(msgOut));
|
|
||||||
|
|
||||||
// Detach
|
|
||||||
File existingSigFile = new File(tempDir, "sig.existing");
|
|
||||||
assertTrue(existingSigFile.createNewFile());
|
|
||||||
PGPainlessCLI.main(new String[] {"inline-detach", "--signatures-out=" + existingSigFile.getAbsolutePath()});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,111 +5,557 @@
|
||||||
package org.pgpainless.cli.commands;
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.OutputStream;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.io.PrintStream;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
import com.ginsberg.junit.exit.FailOnSystemExit;
|
||||||
import org.junit.jupiter.api.AfterAll;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.cli.TestUtils;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
|
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||||
|
import org.pgpainless.key.generation.type.xdh.XDHSpec;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
|
||||||
public class RoundTripEncryptDecryptCmdTest {
|
public class RoundTripEncryptDecryptCmdTest extends CLITest {
|
||||||
|
|
||||||
private static File tempDir;
|
public RoundTripEncryptDecryptCmdTest() {
|
||||||
private static PrintStream originalSout;
|
super(LoggerFactory.getLogger(RoundTripEncryptDecryptCmdTest.class));
|
||||||
|
|
||||||
@BeforeAll
|
|
||||||
public static void prepare() throws IOException {
|
|
||||||
tempDir = TestUtils.createTempDirectory();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: A2EC 077F C977 E15D D799 EFF9 2C0D 3C12 3CF5 1C08\n" +
|
||||||
|
"Comment: Alice <alice@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEY2veRhYJKwYBBAHaRw8BAQdAeJYBoCcnGPQ3nchyyBrWQ83q3hqJnfZn2mqh\n" +
|
||||||
|
"d1M7WwsAAP0R1ELnfdJhXcfjaYPLHzwy1i34FxP5g3tvdgg9Q7VmchActBxBbGlj\n" +
|
||||||
|
"ZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+iI8EExYKAEEFAmNr3kYJECwNPBI89RwI\n" +
|
||||||
|
"FiEEouwHf8l34V3Xme/5LA08Ejz1HAgCngECmwEFFgIDAQAECwkIBwUVCgkICwKZ\n" +
|
||||||
|
"AQAAe6YA/2sO483Vi2Fgs4ejv8FykyO96IVrMoYhw3Od4LyWEyDfAQDi15LxJJm6\n" +
|
||||||
|
"T2sXdENVigdwDJiELxjOtbmivuJutxkWCJxdBGNr3kYSCisGAQQBl1UBBQEBB0CS\n" +
|
||||||
|
"zXjySHqlicxG3QlrVeTIqwKitL1sWsx0MCDmT1C8dAMBCAcAAP9VNkfMQvYAlYSP\n" +
|
||||||
|
"aYEkwEOc8/XpiloVKtPzxwVCPlXFeBDCiHUEGBYKAB0FAmNr3kYCngECmwwFFgID\n" +
|
||||||
|
"AQAECwkIBwUVCgkICwAKCRAsDTwSPPUcCOT4AQDZcN5a/e8Qr+LNBIyXXLgJWGsL\n" +
|
||||||
|
"59nsKHBbDURnxbEnMQEAybS8u+Rsb82yW4CfaA4CLRTC3eDc5Y4QwYWzLogWNwic\n" +
|
||||||
|
"WARja95GFgkrBgEEAdpHDwEBB0DcdwQufWLq6ASku4JWBBd9JplRVhK0cXWuTE73\n" +
|
||||||
|
"uWltuwABAI0bVQXvgDnxTs6kUO7JIWtokM5lI/1bfG4L1YOfnXIgD7CI1QQYFgoA\n" +
|
||||||
|
"fQUCY2veRgKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNr3kYACgkQ\n" +
|
||||||
|
"7NC/hj9lyaWVAwEA3ze1LCi1reGfB5tS3Au6A8aalyk4UV0iVOXxwV5r+E4BAJGz\n" +
|
||||||
|
"ZMFF/iQ/rOcSAsHPp4ggezZALDIkT2Hrn6iLDdsLAAoJECwNPBI89RwIuBIBAMxG\n" +
|
||||||
|
"u/s4maOFozcO4JoCZTsLHGy70SG6UuVQjK0EyJJ1APoDEfK+qTlC7/FoijMA6Ew9\n" +
|
||||||
|
"aesZ2IHgpwA7jlyHSgwLDw==\n" +
|
||||||
|
"=H3HU\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: A2EC 077F C977 E15D D799 EFF9 2C0D 3C12 3CF5 1C08\n" +
|
||||||
|
"Comment: Alice <alice@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"mDMEY2veRhYJKwYBBAHaRw8BAQdAeJYBoCcnGPQ3nchyyBrWQ83q3hqJnfZn2mqh\n" +
|
||||||
|
"d1M7Wwu0HEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz6IjwQTFgoAQQUCY2ve\n" +
|
||||||
|
"RgkQLA08Ejz1HAgWIQSi7Ad/yXfhXdeZ7/ksDTwSPPUcCAKeAQKbAQUWAgMBAAQL\n" +
|
||||||
|
"CQgHBRUKCQgLApkBAAB7pgD/aw7jzdWLYWCzh6O/wXKTI73ohWsyhiHDc53gvJYT\n" +
|
||||||
|
"IN8BAOLXkvEkmbpPaxd0Q1WKB3AMmIQvGM61uaK+4m63GRYIuDgEY2veRhIKKwYB\n" +
|
||||||
|
"BAGXVQEFAQEHQJLNePJIeqWJzEbdCWtV5MirAqK0vWxazHQwIOZPULx0AwEIB4h1\n" +
|
||||||
|
"BBgWCgAdBQJja95GAp4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQLA08Ejz1HAjk\n" +
|
||||||
|
"+AEA2XDeWv3vEK/izQSMl1y4CVhrC+fZ7ChwWw1EZ8WxJzEBAMm0vLvkbG/NsluA\n" +
|
||||||
|
"n2gOAi0Uwt3g3OWOEMGFsy6IFjcIuDMEY2veRhYJKwYBBAHaRw8BAQdA3HcELn1i\n" +
|
||||||
|
"6ugEpLuCVgQXfSaZUVYStHF1rkxO97lpbbuI1QQYFgoAfQUCY2veRgKeAQKbAgUW\n" +
|
||||||
|
"AgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNr3kYACgkQ7NC/hj9lyaWVAwEA3ze1\n" +
|
||||||
|
"LCi1reGfB5tS3Au6A8aalyk4UV0iVOXxwV5r+E4BAJGzZMFF/iQ/rOcSAsHPp4gg\n" +
|
||||||
|
"ezZALDIkT2Hrn6iLDdsLAAoJECwNPBI89RwIuBIBAMxGu/s4maOFozcO4JoCZTsL\n" +
|
||||||
|
"HGy70SG6UuVQjK0EyJJ1APoDEfK+qTlC7/FoijMA6Ew9aesZ2IHgpwA7jlyHSgwL\n" +
|
||||||
|
"Dw==\n" +
|
||||||
|
"=c1PZ\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
@FailOnSystemExit
|
||||||
public void encryptAndDecryptAMessage() throws IOException {
|
public void encryptAndDecryptAMessage() throws IOException {
|
||||||
originalSout = System.out;
|
// Juliets key and cert
|
||||||
File julietKeyFile = new File(tempDir, "juliet.key");
|
File julietKeyFile = pipeStdoutToFile("juliet.key");
|
||||||
assertTrue(julietKeyFile.createNewFile());
|
assertSuccess(executeCommand("generate-key", "Juliet <juliet@capulet.lit>"));
|
||||||
|
|
||||||
File julietCertFile = new File(tempDir, "juliet.asc");
|
pipeFileToStdin(julietKeyFile);
|
||||||
assertTrue(julietCertFile.createNewFile());
|
File julietCertFile = pipeStdoutToFile("juliet.cert");
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
File romeoKeyFile = new File(tempDir, "romeo.key");
|
// Romeos key and cert
|
||||||
assertTrue(romeoKeyFile.createNewFile());
|
File romeoKeyFile = pipeStdoutToFile("romeo.key");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Romeo <romeo@montague.lit>"));
|
||||||
|
|
||||||
File romeoCertFile = new File(tempDir, "romeo.asc");
|
File romeoCertFile = pipeStdoutToFile("romeo.cert");
|
||||||
assertTrue(romeoCertFile.createNewFile());
|
pipeFileToStdin(romeoKeyFile);
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
File msgAscFile = new File(tempDir, "msg.asc");
|
|
||||||
assertTrue(msgAscFile.createNewFile());
|
|
||||||
|
|
||||||
OutputStream julietKeyOut = new FileOutputStream(julietKeyFile);
|
|
||||||
System.setOut(new PrintStream(julietKeyOut));
|
|
||||||
PGPainlessCLI.execute("generate-key", "Juliet Capulet <juliet@capulet.lit>");
|
|
||||||
julietKeyOut.close();
|
|
||||||
|
|
||||||
FileInputStream julietKeyIn = new FileInputStream(julietKeyFile);
|
|
||||||
System.setIn(julietKeyIn);
|
|
||||||
OutputStream julietCertOut = new FileOutputStream(julietCertFile);
|
|
||||||
System.setOut(new PrintStream(julietCertOut));
|
|
||||||
PGPainlessCLI.execute("extract-cert");
|
|
||||||
julietKeyIn.close();
|
|
||||||
julietCertOut.close();
|
|
||||||
|
|
||||||
OutputStream romeoKeyOut = new FileOutputStream(romeoKeyFile);
|
|
||||||
System.setOut(new PrintStream(romeoKeyOut));
|
|
||||||
PGPainlessCLI.execute("generate-key", "Romeo Montague <romeo@montague.lit>");
|
|
||||||
romeoKeyOut.close();
|
|
||||||
|
|
||||||
FileInputStream romeoKeyIn = new FileInputStream(romeoKeyFile);
|
|
||||||
System.setIn(romeoKeyIn);
|
|
||||||
OutputStream romeoCertOut = new FileOutputStream(romeoCertFile);
|
|
||||||
System.setOut(new PrintStream(romeoCertOut));
|
|
||||||
PGPainlessCLI.execute("extract-cert");
|
|
||||||
romeoKeyIn.close();
|
|
||||||
romeoCertOut.close();
|
|
||||||
|
|
||||||
|
// Romeo encrypts signs and encrypts for Juliet and himself
|
||||||
String msg = "Hello World!\n";
|
String msg = "Hello World!\n";
|
||||||
ByteArrayInputStream msgIn = new ByteArrayInputStream(msg.getBytes(StandardCharsets.UTF_8));
|
File encryptedMessageFile = pipeStdoutToFile("msg.asc");
|
||||||
System.setIn(msgIn);
|
pipeStringToStdin(msg);
|
||||||
OutputStream msgAscOut = new FileOutputStream(msgAscFile);
|
assertSuccess(executeCommand("encrypt", "--sign-with", romeoKeyFile.getAbsolutePath(),
|
||||||
System.setOut(new PrintStream(msgAscOut));
|
julietCertFile.getAbsolutePath(), romeoCertFile.getAbsolutePath()));
|
||||||
PGPainlessCLI.execute("encrypt",
|
|
||||||
"--sign-with", romeoKeyFile.getAbsolutePath(),
|
|
||||||
julietCertFile.getAbsolutePath());
|
|
||||||
msgAscOut.close();
|
|
||||||
|
|
||||||
File verifyFile = new File(tempDir, "verify.txt");
|
// Juliet can decrypt and verify with Romeos cert
|
||||||
|
pipeFileToStdin(encryptedMessageFile);
|
||||||
FileInputStream msgAscIn = new FileInputStream(msgAscFile);
|
File verificationsFile = nonExistentFile("verifications");
|
||||||
System.setIn(msgAscIn);
|
ByteArrayOutputStream decrypted = pipeStdoutToStream();
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
assertSuccess(executeCommand("decrypt", "--verifications-out", verificationsFile.getAbsolutePath(),
|
||||||
PrintStream pOut = new PrintStream(out);
|
|
||||||
System.setOut(pOut);
|
|
||||||
PGPainlessCLI.execute("decrypt",
|
|
||||||
"--verify-out", verifyFile.getAbsolutePath(),
|
|
||||||
"--verify-with", romeoCertFile.getAbsolutePath(),
|
"--verify-with", romeoCertFile.getAbsolutePath(),
|
||||||
julietKeyFile.getAbsolutePath());
|
julietKeyFile.getAbsolutePath()));
|
||||||
msgAscIn.close();
|
assertEquals(msg, decrypted.toString());
|
||||||
|
|
||||||
|
// Romeo can decrypt and verify too
|
||||||
|
pipeFileToStdin(encryptedMessageFile);
|
||||||
|
File anotherVerificationsFile = nonExistentFile("anotherVerifications");
|
||||||
|
decrypted = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--verifications-out", anotherVerificationsFile.getAbsolutePath(),
|
||||||
|
"--verify-with", romeoCertFile.getAbsolutePath(),
|
||||||
|
romeoKeyFile.getAbsolutePath()));
|
||||||
|
assertEquals(msg, decrypted.toString());
|
||||||
|
|
||||||
|
String julietsVerif = readStringFromFile(verificationsFile);
|
||||||
|
String romeosVerif = readStringFromFile(anotherVerificationsFile);
|
||||||
|
assertEquals(julietsVerif, romeosVerif);
|
||||||
|
assertFalse(julietsVerif.isEmpty());
|
||||||
|
assertEquals(103, julietsVerif.length()); // 103 is number of symbols in [DATE, FINGER, FINGER] for V4
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMissingArgumentsIfNoArgsSupplied() throws IOException {
|
||||||
|
int exit = executeCommand("encrypt");
|
||||||
|
assertEquals(SOPGPException.MissingArg.EXIT_CODE, exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptingForKeyFails() throws IOException {
|
||||||
|
File notACert = writeFile("key.asc", KEY);
|
||||||
|
|
||||||
|
pipeStringToStdin("Hello, World!");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", notACert.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncrypt_SignWithCertFails() throws IOException {
|
||||||
|
File cert = writeFile("cert.asc", CERT);
|
||||||
|
// noinspection UnnecessaryLocalVariable
|
||||||
|
File notAKey = cert;
|
||||||
|
|
||||||
|
pipeStringToStdin("Hello, World!");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", "--sign-with", notAKey.getAbsolutePath(), cert.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecryptVerifyOut_withoutVerifyWithFails() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY);
|
||||||
|
|
||||||
|
File verifications = nonExistentFile("verifications");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", "--verifications-out", verifications.getAbsolutePath(), key.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.IncompleteVerification.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVerificationsOutAlreadyExistFails() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY);
|
||||||
|
File cert = writeFile("cert.asc", CERT);
|
||||||
|
|
||||||
|
File verifications = writeFile("verifications", "this file is not empty");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", "--verify-with", cert.getAbsolutePath(),
|
||||||
|
"--verifications-out", verifications.getAbsolutePath(),
|
||||||
|
key.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.OutputExists.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSessionKeyOutWritesSessionKeyOut() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY);
|
||||||
|
File sessionKeyFile = nonExistentFile("session.key");
|
||||||
|
|
||||||
|
String plaintext = "Hello, World!\n";
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
String sessionKey = "9:B6FAD96B7ED2DA27D8A36EAEA75DAB7AC587180B14D8A24BD7263524F3DDECC3\n";
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream plaintextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--session-key-out", sessionKeyFile.getAbsolutePath(), key.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(plaintext, plaintextOut.toString());
|
||||||
|
String resultSessionKey = readStringFromFile(sessionKeyFile);
|
||||||
|
assertEquals(sessionKey, resultSessionKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptMessageWithSessionKey() throws IOException {
|
||||||
|
String plaintext = "Hello, World!\n";
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
String sessionKey = "9:B6FAD96B7ED2DA27D8A36EAEA75DAB7AC587180B14D8A24BD7263524F3DDECC3\n";
|
||||||
|
|
||||||
|
File sessionKeyFile = writeFile("session.key", sessionKey);
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream plaintextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--with-session-key", sessionKeyFile.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(plaintext, plaintextOut.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testDecryptWithSessionKeyVerifyWithYieldsExpectedVerifications() throws IOException {
|
||||||
|
String plaintext = "Hello, World!\n";
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
String sessionKey = "9:B6FAD96B7ED2DA27D8A36EAEA75DAB7AC587180B14D8A24BD7263524F3DDECC3\n";
|
||||||
|
|
||||||
|
File cert = writeFile("cert.asc", CERT);
|
||||||
|
File sessionKeyFile = writeFile("session.key", sessionKey);
|
||||||
|
File verifications = nonExistentFile("verifications");
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--with-session-key", sessionKeyFile.getAbsolutePath(),
|
||||||
|
"--verify-with", cert.getAbsolutePath(), "--verifications-out", verifications.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(plaintext, out.toString());
|
||||||
|
String verificationString = readStringFromFile(verifications);
|
||||||
|
assertEquals("2022-11-09T17:22:48Z C0DCEC44B1A173664B05DABCECD0BF863F65C9A5 A2EC077FC977E15DD799EFF92C0D3C123CF51C08\n",
|
||||||
|
verificationString);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptAndDecryptMessageWithPassphrase() throws IOException {
|
||||||
|
File passwordFile = writeFile("password", "c1tiz€n4");
|
||||||
|
String message = "I cannot think of meaningful messages for test vectors rn";
|
||||||
|
|
||||||
|
pipeStringToStdin(message);
|
||||||
|
ByteArrayOutputStream ciphertext = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("encrypt", "--with-password", passwordFile.getAbsolutePath()));
|
||||||
|
|
||||||
|
String ciphertextString = ciphertext.toString();
|
||||||
|
assertTrue(ciphertextString.startsWith("-----BEGIN PGP MESSAGE-----\n"));
|
||||||
|
|
||||||
|
pipeBytesToStdin(ciphertext.toByteArray());
|
||||||
|
ByteArrayOutputStream plaintext = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--with-password", passwordFile.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(message, plaintext.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptWithIncapableCert() throws PGPException,
|
||||||
|
InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
|
||||||
|
.addUserId("No Crypt <no@crypt.key>")
|
||||||
|
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER, KeyFlag.SIGN_DATA))
|
||||||
|
.build();
|
||||||
|
PGPPublicKeyRing cert = PGPainless.extractCertificate(secretKeys);
|
||||||
|
File certFile = writeFile("cert.pgp", cert.getEncoded());
|
||||||
|
|
||||||
|
pipeStringToStdin("Hello, World!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", certFile.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.CertCannotEncrypt.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignWithIncapableKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
|
||||||
|
.addUserId("Cannot Sign <cannot@sign.key>")
|
||||||
|
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||||||
|
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
|
||||||
|
.build();
|
||||||
|
File keyFile = writeFile("key.pgp", secretKeys.getEncoded());
|
||||||
|
File certFile = writeFile("cert.pgp", PGPainless.extractCertificate(secretKeys).getEncoded());
|
||||||
|
|
||||||
|
pipeStringToStdin("Hello, World!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", "--sign-with", keyFile.getAbsolutePath(),
|
||||||
|
certFile.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.KeyCannotSign.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEncryptDecryptRoundTripWithPasswordProtectedKey() throws IOException {
|
||||||
|
// generate password protected key
|
||||||
|
File passwordFile = writeFile("password", "fooBarBaz420");
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key",
|
||||||
|
"--with-key-password", passwordFile.getAbsolutePath(),
|
||||||
|
"Pascal Password <pascal@password.protected>"));
|
||||||
|
|
||||||
|
// extract cert
|
||||||
|
File certFile = pipeStdoutToFile("cert.asc");
|
||||||
|
pipeFileToStdin(keyFile);
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
|
// encrypt and sign message
|
||||||
|
String msg = "Hello, World!\n";
|
||||||
|
pipeStringToStdin(msg);
|
||||||
|
File encryptedFile = pipeStdoutToFile("msg.asc");
|
||||||
|
assertSuccess(executeCommand("encrypt",
|
||||||
|
"--sign-with", keyFile.getAbsolutePath(),
|
||||||
|
"--with-key-password", passwordFile.getAbsolutePath(),
|
||||||
|
"--no-armor",
|
||||||
|
"--as", "binary",
|
||||||
|
certFile.getAbsolutePath()));
|
||||||
|
|
||||||
|
// Decrypt
|
||||||
|
File verificationsFile = nonExistentFile("verifications");
|
||||||
|
pipeFileToStdin(encryptedFile);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt",
|
||||||
|
"--verify-with", certFile.getAbsolutePath(),
|
||||||
|
"--verifications-out", verificationsFile.getAbsolutePath(),
|
||||||
|
"--with-key-password", passwordFile.getAbsolutePath(),
|
||||||
|
keyFile.getAbsolutePath()));
|
||||||
|
|
||||||
assertEquals(msg, out.toString());
|
assertEquals(msg, out.toString());
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
@Test
|
||||||
public static void after() {
|
public void decryptGarbageFails() throws IOException {
|
||||||
System.setOut(originalSout);
|
File keyFile = writeFile("key.asc", KEY);
|
||||||
// CHECKSTYLE:OFF
|
pipeStringToStdin("Some Garbage!");
|
||||||
System.out.println(tempDir.getAbsolutePath());
|
int exitCode = executeCommand("decrypt", keyFile.getAbsolutePath());
|
||||||
// CHECKSTYLE:ON
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decryptMessageWithWrongKeyFails() throws IOException {
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Bob <bob@pgpainless.org>"));
|
||||||
|
// message was *not* created with key above
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", keyFile.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.CannotDecrypt.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptWithPasswordADecryptWithPasswordBFails() throws IOException {
|
||||||
|
File password1 = writeFile("password1", "swordfish");
|
||||||
|
File password2 = writeFile("password2", "orange");
|
||||||
|
|
||||||
|
pipeStringToStdin("Bonjour, le monde!\n");
|
||||||
|
ByteArrayOutputStream ciphertextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("encrypt", "--with-password", password1.getAbsolutePath()));
|
||||||
|
|
||||||
|
pipeBytesToStdin(ciphertextOut.toByteArray());
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", "--with-password", password2.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.CannotDecrypt.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptWithGarbageCertFails() throws IOException {
|
||||||
|
File garbageCert = writeFile("cert.asc", "This is garbage!");
|
||||||
|
|
||||||
|
pipeStringToStdin("Hallo, Welt!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", garbageCert.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encrypt_signWithGarbageKeyFails() throws IOException {
|
||||||
|
File cert = writeFile("cert.asc", CERT);
|
||||||
|
File garbageKey = writeFile("key.asc", "This is not a key!");
|
||||||
|
|
||||||
|
pipeStringToStdin("Salut!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", "--sign-with", garbageKey.getAbsolutePath(),
|
||||||
|
cert.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decrypt_withGarbageKeyFails() throws IOException {
|
||||||
|
File key = writeFile("key.asc", "this is garbage");
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", key.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void decrypt_verifyWithGarbageCertFails() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY);
|
||||||
|
File cert = writeFile("cert.asc", "now this is garbage");
|
||||||
|
String ciphertext = "-----BEGIN PGP MESSAGE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"hF4D831k4umlLu4SAQdAYisjZTDRm217LHQbqjB766tm62CKTkRj3Gd0wYxVRCgw\n" +
|
||||||
|
"48SnOJINCJoPgDsxk2NiJmLCImoiET7IElqxN9htdDXQJwcRK+71r/ZyO4YJpWuX\n" +
|
||||||
|
"0sAAAcEFc3nT+un31sOi8KoBJlc5n+MemntQvcWDs8B87BEW/Ncjrs0s4pJpZKBQ\n" +
|
||||||
|
"/AWc4wLCI3ylfMQJB2pICqaOO3KP3WepgTIw5fuZm6YfriKQi7uZvVx1N+uaCIoa\n" +
|
||||||
|
"K2IVVf/7O9KZJ9GbsGYdpBj9IdaIZiVS3Xi8rwgQl3haI/EeHC3nnCsWyj23Fjt3\n" +
|
||||||
|
"LjbMqpHbSnp8U1cQ8rXavrREaKv69PFeJio6/hRg32TzJqn05dPALRxHMEkxxa4h\n" +
|
||||||
|
"FpVU\n" +
|
||||||
|
"=edS5\n" +
|
||||||
|
"-----END PGP MESSAGE-----";
|
||||||
|
File verificationsFile = nonExistentFile("verifications");
|
||||||
|
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("decrypt", key.getAbsolutePath(),
|
||||||
|
"--verify-with", cert.getAbsolutePath(),
|
||||||
|
"--verifications-out", verificationsFile.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void encryptWithProtectedKey_wrongPassphraseFails() throws IOException {
|
||||||
|
File password = writeFile("passphrase1", "orange");
|
||||||
|
File wrongPassword = writeFile("passphrase2", "blue");
|
||||||
|
|
||||||
|
File keyFile = pipeStdoutToFile("key.asc");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Pedro <pedro@pgpainless.org>",
|
||||||
|
"--with-key-password", password.getAbsolutePath()));
|
||||||
|
|
||||||
|
File certFile = pipeStdoutToFile("cert.asc");
|
||||||
|
pipeFileToStdin(keyFile);
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
|
// Use no passphrase to unlock the key
|
||||||
|
String msg = "Guten Tag, Welt!\n";
|
||||||
|
pipeStringToStdin(msg);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("encrypt", "--sign-with", keyFile.getAbsolutePath(),
|
||||||
|
certFile.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// use wrong passphrase to unlock key when signing message
|
||||||
|
pipeStringToStdin("Guten Tag, Welt!\n");
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
exitCode = executeCommand("encrypt", "--sign-with", keyFile.getAbsolutePath(),
|
||||||
|
"--with-key-password", wrongPassword.getAbsolutePath(),
|
||||||
|
certFile.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// use correct passphrase and encrypt+sign message
|
||||||
|
pipeStringToStdin("Guten Tag, Welt!\n");
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("encrypt", "--sign-with", keyFile.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath(),
|
||||||
|
certFile.getAbsolutePath()));
|
||||||
|
String ciphertext = out.toString();
|
||||||
|
|
||||||
|
// Use no passphrase to decrypt key when decrypting
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
exitCode = executeCommand("decrypt", keyFile.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// Use wrong passphrase to decrypt key when decrypting
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
exitCode = executeCommand("decrypt", "--with-key-password", wrongPassword.getAbsolutePath(),
|
||||||
|
keyFile.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
// User correct passphrase to decrypt key when decrypting
|
||||||
|
pipeStringToStdin(ciphertext);
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("decrypt", "--with-key-password", password.getAbsolutePath(),
|
||||||
|
keyFile.getAbsolutePath()));
|
||||||
|
assertEquals(msg, out.toString());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,236 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
public class RoundTripInlineSignInlineVerifyCmdTest extends CLITest {
|
||||||
|
|
||||||
|
public RoundTripInlineSignInlineVerifyCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(RoundTripInlineSignInlineVerifyCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final String KEY_1_PASSWORD = "takeDemHobbits2Isengard";
|
||||||
|
private static final String KEY_1 = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: 59F4 EC7D 4A87 3E69 7029 8FDE 9FF0 8738 DFC0 0224\n" +
|
||||||
|
"Comment: Legolas <legolas@fellowship.ring>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lIYEY2wKdxYJKwYBBAHaRw8BAQdALfUbOSOsPDg4IgX7Mrub3EtkX0rp02orL/0j\n" +
|
||||||
|
"2VpV1rf+CQMCVICwUO0SkvdgcPdvXO1cW4KIp6HCVVV6VgU5cvBlmrk9PNUQVBkb\n" +
|
||||||
|
"6S7oXQu0CgGwJ+QdbooBQqOjMy2MDy+UXaURTaVyWcmetsZJZzD2wrQhTGVnb2xh\n" +
|
||||||
|
"cyA8bGVnb2xhc0BmZWxsb3dzaGlwLnJpbmc+iI8EExYKAEEFAmNsCncJEJ/whzjf\n" +
|
||||||
|
"wAIkFiEEWfTsfUqHPmlwKY/en/CHON/AAiQCngECmwEFFgIDAQAECwkIBwUVCgkI\n" +
|
||||||
|
"CwKZAQAAE10BAN9tN4Le1p4giS6P/yFuKFlDBOeiq1S4EqwYG7qdcqemAP45O3w4\n" +
|
||||||
|
"3sXliOJBGDR/l/lOMHdPcTOb7VRwWbpIqx8LBJyLBGNsCncSCisGAQQBl1UBBQEB\n" +
|
||||||
|
"B0AMc+7s6uBqAQcDvfKkD5zYbmB9ZfwIjRWQq/XF+g8KQwMBCAf+CQMCVICwUO0S\n" +
|
||||||
|
"kvdgHLmKhKW1xxCNZAqQcIHa9F/cqb6Sq/oVFHj2bEYzmGVvFCVUpP7KJWGTeFT+\n" +
|
||||||
|
"BYK779quIqjxHOfzC3Jmo3BHkUPWYOa0rIh1BBgWCgAdBQJjbAp3Ap4BApsMBRYC\n" +
|
||||||
|
"AwEABAsJCAcFFQoJCAsACgkQn/CHON/AAiRUewD9HtKrCUf3S1yR28emzITWPgJS\n" +
|
||||||
|
"UA5mkzEMnYspV7zU4jgA/R6jj/5QqPszElCQNZGtvsDUwYo10iRlQkxPshcPNakJ\n" +
|
||||||
|
"nIYEY2wKdxYJKwYBBAHaRw8BAQdAYxpRGib/f/tu65gbsV22nmysVVmVgiQuDxyH\n" +
|
||||||
|
"rz7VCi/+CQMCVICwUO0SkvdgOYYbWltjQRDM3SW/Zw/DiZN9MYZYa0MTgs0SHoaM\n" +
|
||||||
|
"5LU7jMxNmPR1UtSqEO36QqW91q4fpEkGrdWE4gwjm1bth8pyYKiSFojVBBgWCgB9\n" +
|
||||||
|
"BQJjbAp3Ap4BApsCBRYCAwEABAsJCAcFFQoJCAtfIAQZFgoABgUCY2wKdwAKCRCW\n" +
|
||||||
|
"K491s9xIMHwKAQDpSWQqiFxFvls9eRGtJ1eQT+L3Z2rDel5zNV44IdTf/wEA0vnJ\n" +
|
||||||
|
"ouSKKuiH6Ck2OEkXbElH6gdQvOCYA7Z9gVeeHQoACgkQn/CHON/AAiSD6QD+LTZx\n" +
|
||||||
|
"NU+t4wQlWOkSsjOLsH/Sk5DZq+4HyQnStlxUJpUBALZFkZps65IP03VkPnQWigfs\n" +
|
||||||
|
"YgztJA1z/rmm3fmFgMMG\n" +
|
||||||
|
"=daDH\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
private static final String CERT_1 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: 59F4 EC7D 4A87 3E69 7029 8FDE 9FF0 8738 DFC0 0224\n" +
|
||||||
|
"Comment: Legolas <legolas@fellowship.ring>\n" +
|
||||||
|
"\n" +
|
||||||
|
"mDMEY2wKdxYJKwYBBAHaRw8BAQdALfUbOSOsPDg4IgX7Mrub3EtkX0rp02orL/0j\n" +
|
||||||
|
"2VpV1re0IUxlZ29sYXMgPGxlZ29sYXNAZmVsbG93c2hpcC5yaW5nPoiPBBMWCgBB\n" +
|
||||||
|
"BQJjbAp3CRCf8Ic438ACJBYhBFn07H1Khz5pcCmP3p/whzjfwAIkAp4BApsBBRYC\n" +
|
||||||
|
"AwEABAsJCAcFFQoJCAsCmQEAABNdAQDfbTeC3taeIIkuj/8hbihZQwTnoqtUuBKs\n" +
|
||||||
|
"GBu6nXKnpgD+OTt8ON7F5YjiQRg0f5f5TjB3T3Ezm+1UcFm6SKsfCwS4OARjbAp3\n" +
|
||||||
|
"EgorBgEEAZdVAQUBAQdADHPu7OrgagEHA73ypA+c2G5gfWX8CI0VkKv1xfoPCkMD\n" +
|
||||||
|
"AQgHiHUEGBYKAB0FAmNsCncCngECmwwFFgIDAQAECwkIBwUVCgkICwAKCRCf8Ic4\n" +
|
||||||
|
"38ACJFR7AP0e0qsJR/dLXJHbx6bMhNY+AlJQDmaTMQydiylXvNTiOAD9HqOP/lCo\n" +
|
||||||
|
"+zMSUJA1ka2+wNTBijXSJGVCTE+yFw81qQm4MwRjbAp3FgkrBgEEAdpHDwEBB0Bj\n" +
|
||||||
|
"GlEaJv9/+27rmBuxXbaebKxVWZWCJC4PHIevPtUKL4jVBBgWCgB9BQJjbAp3Ap4B\n" +
|
||||||
|
"ApsCBRYCAwEABAsJCAcFFQoJCAtfIAQZFgoABgUCY2wKdwAKCRCWK491s9xIMHwK\n" +
|
||||||
|
"AQDpSWQqiFxFvls9eRGtJ1eQT+L3Z2rDel5zNV44IdTf/wEA0vnJouSKKuiH6Ck2\n" +
|
||||||
|
"OEkXbElH6gdQvOCYA7Z9gVeeHQoACgkQn/CHON/AAiSD6QD+LTZxNU+t4wQlWOkS\n" +
|
||||||
|
"sjOLsH/Sk5DZq+4HyQnStlxUJpUBALZFkZps65IP03VkPnQWigfsYgztJA1z/rmm\n" +
|
||||||
|
"3fmFgMMG\n" +
|
||||||
|
"=/lYl\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||||
|
private static final String CERT_1_SIGNING_KEY =
|
||||||
|
"D8906FEB9842569834FEDA9E962B8F75B3DC4830 59F4EC7D4A873E6970298FDE9FF08738DFC00224";
|
||||||
|
|
||||||
|
private static final String KEY_2 = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: AEA0 FD2C 899D 3FC0 7781 5F00 2656 0D2A E53D B86F\n" +
|
||||||
|
"Comment: Gollum <gollum@deep.cave>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEY2wKphYJKwYBBAHaRw8BAQdA9MXACulaJvjIuMKbsc+/fLJ523lODbHmuTpc\n" +
|
||||||
|
"jpPdjaEAAP9Edg7yeIGEeNP0GrndUpNeZyFAXAlCHJObDbS80G6BBw9ktBlHb2xs\n" +
|
||||||
|
"dW0gPGdvbGx1bUBkZWVwLmNhdmU+iI8EExYKAEEFAmNsCqYJECZWDSrlPbhvFiEE\n" +
|
||||||
|
"rqD9LImdP8B3gV8AJlYNKuU9uG8CngECmwEFFgIDAQAECwkIBwUVCgkICwKZAQAA\n" +
|
||||||
|
"KSkBAOMq6ymNH83E5CBA/mn3DYLhnujzC9cVf/iX2zrsdXMvAQCWdfFy/PlGhP3K\n" +
|
||||||
|
"M+ej6WIRsx24Yy/NhNPcRJUzcv6dC5xdBGNsCqYSCisGAQQBl1UBBQEBB0DiN/5n\n" +
|
||||||
|
"AFQafWjnSkKhctFCNkfVRrnAea/2T/D8fYWeYwMBCAcAAP9HbxOhwxqz8I+pwk3e\n" +
|
||||||
|
"kZXNolWqagrYZkpNvqlBb/JJWBGViHUEGBYKAB0FAmNsCqYCngECmwwFFgIDAQAE\n" +
|
||||||
|
"CwkIBwUVCgkICwAKCRAmVg0q5T24bw2EAP4pUHVA2pkVspzEttIaQxdoHcnbwjae\n" +
|
||||||
|
"q12TmWqWDFFvwgD+O2EqHn0iXW49EOQrlP8g+bdWUlT0ZIW3C3Fv7nNA3AScWARj\n" +
|
||||||
|
"bAqmFgkrBgEEAdpHDwEBB0BHsmdF1Q0aU3YRVDeXGb904Nb7H/cxcasDhcbu2FTo\n" +
|
||||||
|
"HAAA/j1+WzozN/3lefo76eyENKkXl4f1rQlUreqytuaTsb0WEq6I1QQYFgoAfQUC\n" +
|
||||||
|
"Y2wKpgKeAQKbAgUWAgMBAAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNsCqYACgkQj73T\n" +
|
||||||
|
"bQGDFnN9OwD/QDDi1qq7DrGlENQf2mPDh36YgM7bREY1vHEbbUNoqy4A/RJzMuwt\n" +
|
||||||
|
"L1M49UzQS7OIGP12/9cT66XPGjpCL+6zLPwCAAoJECZWDSrlPbhvw3ABAOE7/Iit\n" +
|
||||||
|
"ntMexrSK5jCd9JdCCNb2rjR6XA18rXFGOrVBAPwLKAogNFQlP2kUsObTnIaTCro2\n" +
|
||||||
|
"cjK8WE1pfIwQ0ArPCQ==\n" +
|
||||||
|
"=SzrG\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
private static final String CERT_2 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: AEA0 FD2C 899D 3FC0 7781 5F00 2656 0D2A E53D B86F\n" +
|
||||||
|
"Comment: Gollum <gollum@deep.cave>\n" +
|
||||||
|
"\n" +
|
||||||
|
"mDMEY2wKphYJKwYBBAHaRw8BAQdA9MXACulaJvjIuMKbsc+/fLJ523lODbHmuTpc\n" +
|
||||||
|
"jpPdjaG0GUdvbGx1bSA8Z29sbHVtQGRlZXAuY2F2ZT6IjwQTFgoAQQUCY2wKpgkQ\n" +
|
||||||
|
"JlYNKuU9uG8WIQSuoP0siZ0/wHeBXwAmVg0q5T24bwKeAQKbAQUWAgMBAAQLCQgH\n" +
|
||||||
|
"BRUKCQgLApkBAAApKQEA4yrrKY0fzcTkIED+afcNguGe6PML1xV/+JfbOux1cy8B\n" +
|
||||||
|
"AJZ18XL8+UaE/coz56PpYhGzHbhjL82E09xElTNy/p0LuDgEY2wKphIKKwYBBAGX\n" +
|
||||||
|
"VQEFAQEHQOI3/mcAVBp9aOdKQqFy0UI2R9VGucB5r/ZP8Px9hZ5jAwEIB4h1BBgW\n" +
|
||||||
|
"CgAdBQJjbAqmAp4BApsMBRYCAwEABAsJCAcFFQoJCAsACgkQJlYNKuU9uG8NhAD+\n" +
|
||||||
|
"KVB1QNqZFbKcxLbSGkMXaB3J28I2nqtdk5lqlgxRb8IA/jthKh59Il1uPRDkK5T/\n" +
|
||||||
|
"IPm3VlJU9GSFtwtxb+5zQNwEuDMEY2wKphYJKwYBBAHaRw8BAQdAR7JnRdUNGlN2\n" +
|
||||||
|
"EVQ3lxm/dODW+x/3MXGrA4XG7thU6ByI1QQYFgoAfQUCY2wKpgKeAQKbAgUWAgMB\n" +
|
||||||
|
"AAQLCQgHBRUKCQgLXyAEGRYKAAYFAmNsCqYACgkQj73TbQGDFnN9OwD/QDDi1qq7\n" +
|
||||||
|
"DrGlENQf2mPDh36YgM7bREY1vHEbbUNoqy4A/RJzMuwtL1M49UzQS7OIGP12/9cT\n" +
|
||||||
|
"66XPGjpCL+6zLPwCAAoJECZWDSrlPbhvw3ABAOE7/IitntMexrSK5jCd9JdCCNb2\n" +
|
||||||
|
"rjR6XA18rXFGOrVBAPwLKAogNFQlP2kUsObTnIaTCro2cjK8WE1pfIwQ0ArPCQ==\n" +
|
||||||
|
"=j1LR\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||||
|
private static final String CERT_2_SIGNING_KEY =
|
||||||
|
"7A073EDF273C902796D259528FBDD36D01831673 AEA0FD2C899D3FC077815F0026560D2AE53DB86F";
|
||||||
|
|
||||||
|
private static final String MESSAGE = "One does not simply use OpenPGP!\n" +
|
||||||
|
"\n" +
|
||||||
|
"There is only one Lord of the Keys, only one who can bend them to his will. And he does not share power.";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createCleartextSignedMessage() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY_1);
|
||||||
|
File password = writeFile("password", KEY_1_PASSWORD);
|
||||||
|
|
||||||
|
pipeStringToStdin(MESSAGE);
|
||||||
|
ByteArrayOutputStream ciphertextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-sign",
|
||||||
|
"--as", "cleartextsigned",
|
||||||
|
key.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath()));
|
||||||
|
|
||||||
|
String cleartextSigned = ciphertextOut.toString();
|
||||||
|
assertTrue(cleartextSigned.startsWith("-----BEGIN PGP SIGNED MESSAGE-----\n" +
|
||||||
|
"Hash: "));
|
||||||
|
assertTrue(cleartextSigned.contains(MESSAGE));
|
||||||
|
assertTrue(cleartextSigned.contains("\n-----BEGIN PGP SIGNATURE-----\n"));
|
||||||
|
assertTrue(cleartextSigned.endsWith("-----END PGP SIGNATURE-----\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createAndVerifyCleartextSignedMessage() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY_1);
|
||||||
|
File password = writeFile("password", KEY_1_PASSWORD);
|
||||||
|
|
||||||
|
pipeStringToStdin(MESSAGE);
|
||||||
|
ByteArrayOutputStream ciphertextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-sign",
|
||||||
|
"--as", "cleartextsigned",
|
||||||
|
key.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath()));
|
||||||
|
|
||||||
|
File cert = writeFile("cert.asc", CERT_1);
|
||||||
|
File verifications = nonExistentFile("verifications");
|
||||||
|
pipeStringToStdin(ciphertextOut.toString());
|
||||||
|
ByteArrayOutputStream plaintextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-verify",
|
||||||
|
"--verifications-out", verifications.getAbsolutePath(),
|
||||||
|
cert.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(MESSAGE, plaintextOut.toString());
|
||||||
|
String verificationString = readStringFromFile(verifications);
|
||||||
|
assertTrue(verificationString.contains(CERT_1_SIGNING_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createAndVerifyMultiKeyBinarySignedMessage() throws IOException {
|
||||||
|
File key1Pass = writeFile("password", KEY_1_PASSWORD);
|
||||||
|
File key1 = writeFile("key1.asc", KEY_1);
|
||||||
|
File key2 = writeFile("key2.asc", KEY_2);
|
||||||
|
|
||||||
|
pipeStringToStdin(MESSAGE);
|
||||||
|
ByteArrayOutputStream ciphertextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-sign",
|
||||||
|
"--as", "binary",
|
||||||
|
"--no-armor",
|
||||||
|
key2.getAbsolutePath(),
|
||||||
|
"--with-key-password", key1Pass.getAbsolutePath(),
|
||||||
|
key1.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertFalse(ciphertextOut.toString().startsWith("-----BEGIN PGP SIGNED MESSAGE-----\n"));
|
||||||
|
byte[] unarmoredMessage = ciphertextOut.toByteArray();
|
||||||
|
|
||||||
|
File cert1 = writeFile("cert1.asc", CERT_1);
|
||||||
|
File cert2 = writeFile("cert2.asc", CERT_2);
|
||||||
|
File verificationFile = nonExistentFile("verifications");
|
||||||
|
pipeBytesToStdin(unarmoredMessage);
|
||||||
|
ByteArrayOutputStream plaintextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-verify",
|
||||||
|
"--verifications-out", verificationFile.getAbsolutePath(),
|
||||||
|
cert1.getAbsolutePath(), cert2.getAbsolutePath()));
|
||||||
|
|
||||||
|
assertEquals(MESSAGE, plaintextOut.toString());
|
||||||
|
String verification = readStringFromFile(verificationFile);
|
||||||
|
assertTrue(verification.contains(CERT_1_SIGNING_KEY));
|
||||||
|
assertTrue(verification.contains(CERT_2_SIGNING_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createTextSignedMessageInlineDetachAndDetachedVerify() throws IOException {
|
||||||
|
File key = writeFile("key.asc", KEY_1);
|
||||||
|
File password = writeFile("password", KEY_1_PASSWORD);
|
||||||
|
|
||||||
|
pipeStringToStdin(MESSAGE);
|
||||||
|
ByteArrayOutputStream ciphertextOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-sign",
|
||||||
|
"--as", "cleartextsigned",
|
||||||
|
key.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath()));
|
||||||
|
|
||||||
|
File sigFile = nonExistentFile("sig.asc");
|
||||||
|
pipeStringToStdin(ciphertextOut.toString());
|
||||||
|
ByteArrayOutputStream msgOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("inline-detach",
|
||||||
|
"--signatures-out", sigFile.getAbsolutePath()));
|
||||||
|
assertEquals(MESSAGE, msgOut.toString());
|
||||||
|
|
||||||
|
File cert = writeFile("cert.asc", CERT_1);
|
||||||
|
pipeStringToStdin(msgOut.toString());
|
||||||
|
ByteArrayOutputStream verificationsOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("verify",
|
||||||
|
sigFile.getAbsolutePath(),
|
||||||
|
cert.getAbsolutePath()));
|
||||||
|
|
||||||
|
String verificationString = verificationsOut.toString();
|
||||||
|
assertTrue(verificationString.contains(CERT_1_SIGNING_KEY));
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,129 +5,321 @@
|
||||||
package org.pgpainless.cli.commands;
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.FileReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.OutputStream;
|
|
||||||
import java.io.PrintStream;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.security.InvalidAlgorithmParameterException;
|
import java.security.InvalidAlgorithmParameterException;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import com.ginsberg.junit.exit.FailOnSystemExit;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.util.io.Streams;
|
|
||||||
import org.junit.jupiter.api.AfterAll;
|
|
||||||
import org.junit.jupiter.api.BeforeAll;
|
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.pgpainless.PGPainless;
|
import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.cli.PGPainlessCLI;
|
import org.pgpainless.algorithm.KeyFlag;
|
||||||
import org.pgpainless.cli.TestUtils;
|
|
||||||
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
import org.pgpainless.key.OpenPgpV4Fingerprint;
|
||||||
|
import org.pgpainless.key.generation.KeySpec;
|
||||||
|
import org.pgpainless.key.generation.type.KeyType;
|
||||||
|
import org.pgpainless.key.generation.type.eddsa.EdDSACurve;
|
||||||
|
import org.pgpainless.key.generation.type.xdh.XDHSpec;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
import org.pgpainless.key.util.KeyRingUtils;
|
import org.slf4j.LoggerFactory;
|
||||||
|
import sop.exception.SOPGPException;
|
||||||
|
import sop.util.UTCUtil;
|
||||||
|
|
||||||
public class RoundTripSignVerifyCmdTest {
|
public class RoundTripSignVerifyCmdTest extends CLITest {
|
||||||
|
|
||||||
private static File tempDir;
|
public RoundTripSignVerifyCmdTest() {
|
||||||
private static PrintStream originalSout;
|
super(LoggerFactory.getLogger(RoundTripSignVerifyCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
@BeforeAll
|
private static final String KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
public static void prepare() throws IOException {
|
"Version: PGPainless\n" +
|
||||||
tempDir = TestUtils.createTempDirectory();
|
"Comment: 9DA0 9423 C9F9 4BA4 CCA3 0951 099B 11BF 296A 373E\n" +
|
||||||
|
"Comment: Sigmund <sigmund@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"lFgEY2vzkhYJKwYBBAHaRw8BAQdA+Z2OAFQf0k64Au7hIZfXh/ijclabddvwh7Nh\n" +
|
||||||
|
"kedJ3ZUAAQCZy5p1cvQvRIWUopHwhnrD/oVAa1dNT/nA3cihQ5gkZBHPtCBTaWdt\n" +
|
||||||
|
"dW5kIDxzaWdtdW5kQHBncGFpbmxlc3Mub3JnPoiPBBMWCgBBBQJja/OSCRAJmxG/\n" +
|
||||||
|
"KWo3PhYhBJ2glCPJ+UukzKMJUQmbEb8pajc+Ap4BApsBBRYCAwEABAsJCAcFFQoJ\n" +
|
||||||
|
"CAsCmQEAACM9AP9APloI2waD5gXsJqzenRVU4n/VmZUvcdUyhlbpab/0HQEAlaTw\n" +
|
||||||
|
"ZvxVyaf8EMFSJOY+LcgacHaZDHRPA1nS3bIfKwycXQRja/OSEgorBgEEAZdVAQUB\n" +
|
||||||
|
"AQdA1WL4QKgRxbvzW91ICM6PoICSNh2QHK6j0pIdN/cqXz0DAQgHAAD/bOk3WqbF\n" +
|
||||||
|
"QAE8xxm0w/KDZzL1N0yPcBQ5z4XKmu77FCgQ04h1BBgWCgAdBQJja/OSAp4BApsM\n" +
|
||||||
|
"BRYCAwEABAsJCAcFFQoJCAsACgkQCZsRvylqNz6rgQEAzoG6HnPCYi2i2c6/ufuy\n" +
|
||||||
|
"pBkLby2u1JjD0CWSbrM4dZ0A/j/pI4a9b8LcrZcuY2QwHqsXPAJp8QtOOQN6gTvN\n" +
|
||||||
|
"WcQNnFgEY2vzkhYJKwYBBAHaRw8BAQdAsxcDCvst/GbWxQvvOpChSvmbqWeuBgm3\n" +
|
||||||
|
"1vRoujFVFcYAAP9Ww46yfWipb8OivTSX+PvgdUhEeVgxENpsyOQLLhQP/RFziNUE\n" +
|
||||||
|
"GBYKAH0FAmNr85ICngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJja/OS\n" +
|
||||||
|
"AAoJENqfQTmGIR3GtsMBAL+b1Zo5giQKJGEyx5aGwAz3AwtGiT6QDS9FH6HyM855\n" +
|
||||||
|
"AP4uAXDiaNxYTugqnG471jYX/hhJqIROeDGrEIkkAp+qDwAKCRAJmxG/KWo3PhOX\n" +
|
||||||
|
"AP45LPV6I4+D3h8etdiEA2DVvNcpRA8l4WkNcq4q8H1SjwD/c/rX3FCUIWLlAHoR\n" +
|
||||||
|
"WxCFj+gDgqDNLzwoA4iNo1VMtQc=\n" +
|
||||||
|
"=/Np6\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
|
||||||
|
private static final String CERT = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"Comment: 9DA0 9423 C9F9 4BA4 CCA3 0951 099B 11BF 296A 373E\n" +
|
||||||
|
"Comment: Sigmund <sigmund@pgpainless.org>\n" +
|
||||||
|
"\n" +
|
||||||
|
"mDMEY2vzkhYJKwYBBAHaRw8BAQdA+Z2OAFQf0k64Au7hIZfXh/ijclabddvwh7Nh\n" +
|
||||||
|
"kedJ3ZW0IFNpZ211bmQgPHNpZ211bmRAcGdwYWlubGVzcy5vcmc+iI8EExYKAEEF\n" +
|
||||||
|
"AmNr85IJEAmbEb8pajc+FiEEnaCUI8n5S6TMowlRCZsRvylqNz4CngECmwEFFgID\n" +
|
||||||
|
"AQAECwkIBwUVCgkICwKZAQAAIz0A/0A+WgjbBoPmBewmrN6dFVTif9WZlS9x1TKG\n" +
|
||||||
|
"Vulpv/QdAQCVpPBm/FXJp/wQwVIk5j4tyBpwdpkMdE8DWdLdsh8rDLg4BGNr85IS\n" +
|
||||||
|
"CisGAQQBl1UBBQEBB0DVYvhAqBHFu/Nb3UgIzo+ggJI2HZAcrqPSkh039ypfPQMB\n" +
|
||||||
|
"CAeIdQQYFgoAHQUCY2vzkgKeAQKbDAUWAgMBAAQLCQgHBRUKCQgLAAoJEAmbEb8p\n" +
|
||||||
|
"ajc+q4EBAM6Buh5zwmItotnOv7n7sqQZC28trtSYw9Alkm6zOHWdAP4/6SOGvW/C\n" +
|
||||||
|
"3K2XLmNkMB6rFzwCafELTjkDeoE7zVnEDbgzBGNr85IWCSsGAQQB2kcPAQEHQLMX\n" +
|
||||||
|
"Awr7Lfxm1sUL7zqQoUr5m6lnrgYJt9b0aLoxVRXGiNUEGBYKAH0FAmNr85ICngEC\n" +
|
||||||
|
"mwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJja/OSAAoJENqfQTmGIR3GtsMB\n" +
|
||||||
|
"AL+b1Zo5giQKJGEyx5aGwAz3AwtGiT6QDS9FH6HyM855AP4uAXDiaNxYTugqnG47\n" +
|
||||||
|
"1jYX/hhJqIROeDGrEIkkAp+qDwAKCRAJmxG/KWo3PhOXAP45LPV6I4+D3h8etdiE\n" +
|
||||||
|
"A2DVvNcpRA8l4WkNcq4q8H1SjwD/c/rX3FCUIWLlAHoRWxCFj+gDgqDNLzwoA4iN\n" +
|
||||||
|
"o1VMtQc=\n" +
|
||||||
|
"=KuJ4\n" +
|
||||||
|
"-----END PGP PUBLIC KEY BLOCK-----";
|
||||||
|
private static final String PLAINTEXT = "Hello, World!\n";
|
||||||
|
private static final String BINARY_SIG = "-----BEGIN PGP SIGNATURE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"iHUEABYKACcFAmNr9BgJENqfQTmGIR3GFiEEREwQqwEe+EJMg/Cp2p9BOYYhHcYA\n" +
|
||||||
|
"AKocAP48P2C3TU33T3Zy73clw0eBa1oW9pwxTGuFxhgOBzmoSwEArj0781GlpTB0\n" +
|
||||||
|
"Vnr2PjPYEqzB+ZuOzOnGhsVGob4c3Ao=\n" +
|
||||||
|
"=VWAZ\n" +
|
||||||
|
"-----END PGP SIGNATURE-----";
|
||||||
|
private static final String BINARY_SIG_VERIFICATION =
|
||||||
|
"2022-11-09T18:40:24Z 444C10AB011EF8424C83F0A9DA9F413986211DC6 9DA09423C9F94BA4CCA30951099B11BF296A373E\n";
|
||||||
|
private static final String TEXT_SIG = "-----BEGIN PGP SIGNATURE-----\n" +
|
||||||
|
"Version: PGPainless\n" +
|
||||||
|
"\n" +
|
||||||
|
"iHUEARYKACcFAmNr9E4JENqfQTmGIR3GFiEEREwQqwEe+EJMg/Cp2p9BOYYhHcYA\n" +
|
||||||
|
"AG+CAQD1B3GAAlyxahSiGhvJv7YAI1m6qGcI7dIXcV7FkAFPSgEAlZ0UpCC8oGR+\n" +
|
||||||
|
"hi/mQlex4z0hDWSA4abAjclPTJ+qkAI=\n" +
|
||||||
|
"=s5xn\n" +
|
||||||
|
"-----END PGP SIGNATURE-----";
|
||||||
|
private static final String TEXT_SIG_VERIFICATION =
|
||||||
|
"2022-11-09T18:41:18Z 444C10AB011EF8424C83F0A9DA9F413986211DC6 9DA09423C9F94BA4CCA30951099B11BF296A373E\n";
|
||||||
|
private static final Date TEXT_SIG_CREATION = UTCUtil.parseUTCDate("2022-11-09T18:41:18Z");
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void createArmoredSignature() throws IOException {
|
||||||
|
File keyFile = writeFile("key.asc", KEY);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("sign", "--as", "text", keyFile.getAbsolutePath()));
|
||||||
|
assertTrue(out.toString().startsWith("-----BEGIN PGP SIGNATURE-----\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@FailOnSystemExit
|
public void createUnarmoredSignature() throws IOException {
|
||||||
public void testSignatureCreationAndVerification() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
|
File keyFile = writeFile("key.asc", KEY);
|
||||||
originalSout = System.out;
|
pipeStringToStdin(PLAINTEXT);
|
||||||
InputStream originalIn = System.in;
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("sign", "--no-armor", keyFile.getAbsolutePath()));
|
||||||
|
assertFalse(out.toString().startsWith("-----BEGIN PGP SIGNATURE-----\n"));
|
||||||
|
}
|
||||||
|
|
||||||
// Write alice key to disc
|
@Test
|
||||||
File aliceKeyFile = new File(tempDir, "alice.key");
|
public void unarmorArmoredSigAndVerify() throws IOException {
|
||||||
assertTrue(aliceKeyFile.createNewFile());
|
File certFile = writeFile("cert.asc", CERT);
|
||||||
PGPSecretKeyRing aliceKeys = PGPainless.generateKeyRing()
|
|
||||||
.modernKeyRing("alice");
|
|
||||||
OutputStream aliceKeyOut = new FileOutputStream(aliceKeyFile);
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(aliceKeys.getEncoded()), aliceKeyOut);
|
|
||||||
aliceKeyOut.close();
|
|
||||||
|
|
||||||
// Write alice pub key to disc
|
pipeStringToStdin(BINARY_SIG);
|
||||||
File aliceCertFile = new File(tempDir, "alice.pub");
|
File unarmoredSigFile = pipeStdoutToFile("sig.pgp");
|
||||||
assertTrue(aliceCertFile.createNewFile());
|
assertSuccess(executeCommand("dearmor"));
|
||||||
PGPPublicKeyRing alicePub = KeyRingUtils.publicKeyRingFrom(aliceKeys);
|
|
||||||
OutputStream aliceCertOut = new FileOutputStream(aliceCertFile);
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(alicePub.getEncoded()), aliceCertOut);
|
|
||||||
aliceCertOut.close();
|
|
||||||
|
|
||||||
// Write test data to disc
|
pipeStringToStdin(PLAINTEXT);
|
||||||
String data = "If privacy is outlawed, only outlaws will have privacy.\n";
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
File dataFile = new File(tempDir, "data");
|
assertSuccess(executeCommand("verify", unarmoredSigFile.getAbsolutePath(), certFile.getAbsolutePath()));
|
||||||
assertTrue(dataFile.createNewFile());
|
|
||||||
FileOutputStream dataOut = new FileOutputStream(dataFile);
|
|
||||||
Streams.pipeAll(new ByteArrayInputStream(data.getBytes(StandardCharsets.UTF_8)), dataOut);
|
|
||||||
dataOut.close();
|
|
||||||
|
|
||||||
// Define micalg output file
|
assertEquals(BINARY_SIG_VERIFICATION, out.toString());
|
||||||
File micalgOut = new File(tempDir, "micalg");
|
}
|
||||||
|
|
||||||
// Sign test data
|
@Test
|
||||||
FileInputStream dataIn = new FileInputStream(dataFile);
|
public void testNotBefore() throws IOException {
|
||||||
System.setIn(dataIn);
|
File cert = writeFile("cert.asc", CERT);
|
||||||
File sigFile = new File(tempDir, "sig.asc");
|
File sigFile = writeFile("sig.asc", TEXT_SIG);
|
||||||
assertTrue(sigFile.createNewFile());
|
Date plus1Minute = new Date(TEXT_SIG_CREATION.getTime() + 1000 * 60);
|
||||||
FileOutputStream sigOut = new FileOutputStream(sigFile);
|
|
||||||
System.setOut(new PrintStream(sigOut));
|
pipeStringToStdin(PLAINTEXT);
|
||||||
PGPainlessCLI.execute("sign", "--armor", "--micalg-out", micalgOut.getAbsolutePath(), aliceKeyFile.getAbsolutePath());
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
sigOut.close();
|
int exitCode = executeCommand("verify", sigFile.getAbsolutePath(), cert.getAbsolutePath(),
|
||||||
|
"--not-before", UTCUtil.formatUTCDate(plus1Minute));
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.NoSignature.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
Date minus1Minute = new Date(TEXT_SIG_CREATION.getTime() - 1000 * 60);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
exitCode = executeCommand("verify", sigFile.getAbsolutePath(), cert.getAbsolutePath(),
|
||||||
|
"--not-before", UTCUtil.formatUTCDate(minus1Minute));
|
||||||
|
|
||||||
|
assertSuccess(exitCode);
|
||||||
|
assertEquals(TEXT_SIG_VERIFICATION, out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testNotAfter() throws IOException {
|
||||||
|
File cert = writeFile("cert.asc", CERT);
|
||||||
|
File sigFile = writeFile("sig.asc", TEXT_SIG);
|
||||||
|
|
||||||
|
Date minus1Minute = new Date(TEXT_SIG_CREATION.getTime() - 1000 * 60);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("verify", sigFile.getAbsolutePath(), cert.getAbsolutePath(),
|
||||||
|
"--not-after", UTCUtil.formatUTCDate(minus1Minute));
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.NoSignature.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
|
||||||
|
Date plus1Minute = new Date(TEXT_SIG_CREATION.getTime() + 1000 * 60);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
out = pipeStdoutToStream();
|
||||||
|
exitCode = executeCommand("verify", sigFile.getAbsolutePath(), cert.getAbsolutePath(),
|
||||||
|
"--not-after", UTCUtil.formatUTCDate(plus1Minute));
|
||||||
|
|
||||||
|
assertSuccess(exitCode);
|
||||||
|
assertEquals(TEXT_SIG_VERIFICATION, out.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignWithIncapableKey() throws IOException, PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException {
|
||||||
|
PGPSecretKeyRing secretKeys = PGPainless.buildKeyRing()
|
||||||
|
.addUserId("Cannot Sign <cannot@sign.key>")
|
||||||
|
.setPrimaryKey(KeySpec.getBuilder(KeyType.EDDSA(EdDSACurve._Ed25519), KeyFlag.CERTIFY_OTHER))
|
||||||
|
.addSubkey(KeySpec.getBuilder(KeyType.XDH(XDHSpec._X25519), KeyFlag.ENCRYPT_COMMS, KeyFlag.ENCRYPT_STORAGE))
|
||||||
|
.build();
|
||||||
|
File keyFile = writeFile("key.pgp", secretKeys.getEncoded());
|
||||||
|
|
||||||
|
pipeStringToStdin("Hello, World!\n");
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("sign", keyFile.getAbsolutePath());
|
||||||
|
|
||||||
|
assertEquals(SOPGPException.KeyCannotSign.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSignatureCreationAndVerification()
|
||||||
|
throws IOException {
|
||||||
|
// Create key and cert
|
||||||
|
File aliceKeyFile = pipeStdoutToFile("alice.key");
|
||||||
|
assertSuccess(executeCommand("generate-key", "Alice <alice@pgpainless.org>"));
|
||||||
|
File aliceCertFile = pipeStdoutToFile("alice.cert");
|
||||||
|
pipeFileToStdin(aliceKeyFile);
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
|
File micalgOut = nonExistentFile("micalg");
|
||||||
|
String msg = "If privacy is outlawed, only outlaws will have privacy.\n";
|
||||||
|
File dataFile = writeFile("data", msg);
|
||||||
|
|
||||||
|
// sign data
|
||||||
|
File sigFile = pipeStdoutToFile("sig.asc");
|
||||||
|
pipeFileToStdin(dataFile);
|
||||||
|
assertSuccess(executeCommand("sign",
|
||||||
|
"--armor",
|
||||||
|
"--as", "binary",
|
||||||
|
"--micalg-out", micalgOut.getAbsolutePath(),
|
||||||
|
aliceKeyFile.getAbsolutePath()));
|
||||||
|
|
||||||
// verify test data signature
|
// verify test data signature
|
||||||
ByteArrayOutputStream verifyOut = new ByteArrayOutputStream();
|
pipeFileToStdin(dataFile);
|
||||||
System.setOut(new PrintStream(verifyOut));
|
ByteArrayOutputStream verificationsOut = pipeStdoutToStream();
|
||||||
dataIn = new FileInputStream(dataFile);
|
assertSuccess(executeCommand("verify", sigFile.getAbsolutePath(), aliceCertFile.getAbsolutePath()));
|
||||||
System.setIn(dataIn);
|
|
||||||
PGPainlessCLI.execute("verify", sigFile.getAbsolutePath(), aliceCertFile.getAbsolutePath());
|
|
||||||
dataIn.close();
|
|
||||||
|
|
||||||
// Test verification output
|
// Test verification output
|
||||||
|
|
||||||
|
PGPPublicKeyRing cert = PGPainless.readKeyRing().publicKeyRing(readBytesFromFile(aliceCertFile));
|
||||||
|
KeyRingInfo info = PGPainless.inspectKeyRing(cert);
|
||||||
|
|
||||||
// [date] [signing-key-fp] [primary-key-fp] signed by [key.pub]
|
// [date] [signing-key-fp] [primary-key-fp] signed by [key.pub]
|
||||||
String verification = verifyOut.toString();
|
String verification = verificationsOut.toString();
|
||||||
String[] split = verification.split(" ");
|
String[] split = verification.split(" ");
|
||||||
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(aliceKeys);
|
OpenPgpV4Fingerprint primaryKeyFingerprint = new OpenPgpV4Fingerprint(cert);
|
||||||
OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(new KeyRingInfo(alicePub, new Date()).getSigningSubkeys().get(0));
|
OpenPgpV4Fingerprint signingKeyFingerprint = new OpenPgpV4Fingerprint(info.getSigningSubkeys().get(0));
|
||||||
assertEquals(signingKeyFingerprint.toString(), split[1].trim(), verification);
|
assertEquals(signingKeyFingerprint.toString(), split[1].trim(), verification);
|
||||||
assertEquals(primaryKeyFingerprint.toString(), split[2].trim());
|
assertEquals(primaryKeyFingerprint.toString(), split[2].trim());
|
||||||
|
|
||||||
// Test micalg output
|
// Test micalg output
|
||||||
assertTrue(micalgOut.exists());
|
String content = readStringFromFile(micalgOut);
|
||||||
FileReader fileReader = new FileReader(micalgOut);
|
assertEquals("pgp-sha512", content);
|
||||||
BufferedReader bufferedReader = new BufferedReader(fileReader);
|
|
||||||
String line = bufferedReader.readLine();
|
|
||||||
assertNull(bufferedReader.readLine());
|
|
||||||
bufferedReader.close();
|
|
||||||
assertEquals("pgp-sha512", line);
|
|
||||||
|
|
||||||
System.setIn(originalIn);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterAll
|
private static final String PROTECTED_KEY = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
|
||||||
public static void after() {
|
"Version: PGPainless\n" +
|
||||||
System.setOut(originalSout);
|
"Comment: 738E EAB2 503D 322D 613A C42A B18E 8BF8 884F C050\n" +
|
||||||
// CHECKSTYLE:OFF
|
"Comment: Axel <axel@pgpainless.org>\n" +
|
||||||
System.out.println(tempDir.getAbsolutePath());
|
"\n" +
|
||||||
// CHECKSTYLE:ON
|
"lIYEY2v6aRYJKwYBBAHaRw8BAQdA3PXtH19zYpVQ9zTU3zlY+iXUptelAO3z4vK/\n" +
|
||||||
|
"M2FkmrP+CQMCYgVa6K+InVJguITSDIA+HQ6vhOZ5Dbanqx7GFbJbJLD2fWrxhTSr\n" +
|
||||||
|
"BUWGaUWTqN647auD/kUI8phH1cedVL6CzVR+YWvaWj9zZHr/CYXLobQaQXhlbCA8\n" +
|
||||||
|
"YXhlbEBwZ3BhaW5sZXNzLm9yZz6IjwQTFgoAQQUCY2v6aQkQsY6L+IhPwFAWIQRz\n" +
|
||||||
|
"juqyUD0yLWE6xCqxjov4iE/AUAKeAQKbAQUWAgMBAAQLCQgHBRUKCQgLApkBAACq\n" +
|
||||||
|
"zgEAkxB+dUI7Jjcg5zRvT1EfE9DKCI1qTsxOAU/ZXLcSXLkBAJtWRsyptetZvjzB\n" +
|
||||||
|
"Ze2A7ArOl4q+IvKvun/d783YyRMInIsEY2v6aRIKKwYBBAGXVQEFAQEHQPFmlZ+o\n" +
|
||||||
|
"jCGEo2X0474vJfRG7blctuZXmCbC0sLO7MgzAwEIB/4JAwJiBVror4idUmDFhBq4\n" +
|
||||||
|
"lEhJxjCVc6aSD6+EWRT3YdplqCmNdynnrPombUFst6LfJFzns3H3d0rCeXHfQr93\n" +
|
||||||
|
"GrHTLkHfW8G3x0PJJPiqFkBviHUEGBYKAB0FAmNr+mkCngECmwwFFgIDAQAECwkI\n" +
|
||||||
|
"BwUVCgkICwAKCRCxjov4iE/AUNC2AP9WDx4lHt9oYFLSrM8vMLRFI31U8TkYrtCe\n" +
|
||||||
|
"pYICE76cIAEA5+wEbtE5vQrLxOqIRueVVdzwK9kTeMvSIQfc9PNoyQKchgRja/pp\n" +
|
||||||
|
"FgkrBgEEAdpHDwEBB0CyAEVlCUbFr3dBBG3MQ84hjCPfYqSx9kYsTN8j5Og6uP4J\n" +
|
||||||
|
"AwJiBVror4idUmCIFuAYXia0YpEhEpB/Lrn/D6/WAUPEgZjNLMvJzL//EmhkWfEa\n" +
|
||||||
|
"OfQz/fslj1erWNjLKNiW5C/TvGapDfjbn596AkNlcd1JiNUEGBYKAH0FAmNr+mkC\n" +
|
||||||
|
"ngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBQJja/ppAAoJELRgil1uCuQj\n" +
|
||||||
|
"VUYBAJecbedwwqWQITVqucEBIraTRoc6ZGkN8jytDp8z9CsBAQDrb/W/J/kze6ln\n" +
|
||||||
|
"nRyJSriWF3SjcKOGIRkUslmdJEPPCQAKCRCxjov4iE/AUAvbAQDBBgQFG8acTT5L\n" +
|
||||||
|
"cyIi1Ix9/XBG7G23SSs6l7Beap8M+wEAmK13NYuq7Mv/mct8iIKZbBFH9aAiY+nX\n" +
|
||||||
|
"3Uct4Q5f0w0=\n" +
|
||||||
|
"=K65R\n" +
|
||||||
|
"-----END PGP PRIVATE KEY BLOCK-----";
|
||||||
|
private static final String PASSPHRASE = "orange";
|
||||||
|
private static final String SIGNING_KEY = "9846F3606EE875FB77EC8808B4608A5D6E0AE423 738EEAB2503D322D613AC42AB18E8BF8884FC050";
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void signWithProtectedKey_missingPassphraseFails() throws IOException {
|
||||||
|
File key = writeFile("key.asc", PROTECTED_KEY);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("sign", key.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void signWithProtectedKey_wrongPassphraseFails() throws IOException {
|
||||||
|
File password = writeFile("password", "blue");
|
||||||
|
File key = writeFile("key.asc", PROTECTED_KEY);
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
int exitCode = executeCommand("sign", key.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath());
|
||||||
|
assertEquals(SOPGPException.KeyIsProtected.EXIT_CODE, exitCode);
|
||||||
|
assertEquals(0, out.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void signWithProtectedKey() throws IOException {
|
||||||
|
File password = writeFile("password", PASSPHRASE);
|
||||||
|
File key = writeFile("key.asc", PROTECTED_KEY);
|
||||||
|
pipeStringToStdin(PROTECTED_KEY);
|
||||||
|
File cert = pipeStdoutToFile("cert.asc");
|
||||||
|
assertSuccess(executeCommand("extract-cert"));
|
||||||
|
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
File sigFile = pipeStdoutToFile("sig.asc");
|
||||||
|
assertSuccess(executeCommand("sign", key.getAbsolutePath(),
|
||||||
|
"--with-key-password", password.getAbsolutePath()));
|
||||||
|
|
||||||
|
pipeStringToStdin(PLAINTEXT);
|
||||||
|
ByteArrayOutputStream verificationOut = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("verify", sigFile.getAbsolutePath(), cert.getAbsolutePath()));
|
||||||
|
assertTrue(verificationOut.toString().contains(SIGNING_KEY));
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
// SPDX-FileCopyrightText: 2022 Paul Schaub <vanitasvitae@fsfe.org>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package org.pgpainless.cli.commands;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
public class VersionCmdTest extends CLITest {
|
||||||
|
|
||||||
|
public VersionCmdTest() {
|
||||||
|
super(LoggerFactory.getLogger(VersionCmdTest.class));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testVersion() throws IOException {
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("version"));
|
||||||
|
assertTrue(out.toString().startsWith("PGPainless-SOP "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testGetBackendVersion() throws IOException {
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("version", "--backend"));
|
||||||
|
assertTrue(out.toString().startsWith("Bouncy Castle "));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExtendedVersion() throws IOException {
|
||||||
|
ByteArrayOutputStream out = pipeStdoutToStream();
|
||||||
|
assertSuccess(executeCommand("version", "--extended"));
|
||||||
|
String info = out.toString();
|
||||||
|
assertTrue(info.startsWith("PGPainless-SOP "));
|
||||||
|
assertTrue(info.contains("Bouncy Castle"));
|
||||||
|
assertTrue(info.contains("Stateless OpenPGP Protocol"));
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue