pgpainless/pgpainless-cli/src/test/java/org/pgpainless/cli/commands/RoundTripEncryptDecryptCmdT...

676 lines
32 KiB
Java

// 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.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
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.PGPSecretKeyRing;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
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_legacy.EdDSALegacyCurve;
import org.pgpainless.key.generation.type.xdh_legacy.XDHLegacySpec;
import org.slf4j.LoggerFactory;
import sop.exception.SOPGPException;
public class RoundTripEncryptDecryptCmdTest extends CLITest {
public RoundTripEncryptDecryptCmdTest() {
super(LoggerFactory.getLogger(RoundTripEncryptDecryptCmdTest.class));
}
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
@FailOnSystemExit
public void encryptAndDecryptAMessage() throws IOException {
// Juliets key and cert
File julietKeyFile = pipeStdoutToFile("juliet.key");
assertSuccess(executeCommand("generate-key", "Juliet <juliet@capulet.lit>"));
pipeFileToStdin(julietKeyFile);
File julietCertFile = pipeStdoutToFile("juliet.cert");
assertSuccess(executeCommand("extract-cert"));
// Romeos key and cert
File romeoKeyFile = pipeStdoutToFile("romeo.key");
assertSuccess(executeCommand("generate-key", "Romeo <romeo@montague.lit>"));
File romeoCertFile = pipeStdoutToFile("romeo.cert");
pipeFileToStdin(romeoKeyFile);
assertSuccess(executeCommand("extract-cert"));
// Romeo encrypts signs and encrypts for Juliet and himself
String msg = "Hello World!\n";
File encryptedMessageFile = pipeStdoutToFile("msg.asc");
pipeStringToStdin(msg);
assertSuccess(executeCommand("encrypt", "--sign-with", romeoKeyFile.getAbsolutePath(),
julietCertFile.getAbsolutePath(), romeoCertFile.getAbsolutePath()));
// Juliet can decrypt and verify with Romeos cert
pipeFileToStdin(encryptedMessageFile);
File verificationsFile = nonExistentFile("verifications");
ByteArrayOutputStream decrypted = pipeStdoutToStream();
assertSuccess(executeCommand("decrypt", "--verifications-out", verificationsFile.getAbsolutePath(),
"--verify-with", romeoCertFile.getAbsolutePath(),
julietKeyFile.getAbsolutePath()));
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(115, julietsVerif.length()); // 115 is number of symbols in [DATE, FINGER, FINGER, MODE] for V4
}
@Test
public void testMissingArgumentsIfNoArgsSupplied() throws IOException {
int exit = executeCommand("encrypt");
assertEquals(SOPGPException.MissingArg.EXIT_CODE, exit);
}
@Test
@Disabled("Disabled, since we now read certificates from secret keys")
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 mode:binary\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_LEGACY(EdDSALegacyCurve._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_LEGACY(EdDSALegacyCurve._Ed25519), KeyFlag.CERTIFY_OTHER))
.addSubkey(KeySpec.getBuilder(
KeyType.XDH_LEGACY(XDHLegacySpec._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());
}
@Test
public void decryptGarbageFails() throws IOException {
File keyFile = writeFile("key.asc", KEY);
pipeStringToStdin("Some Garbage!");
int exitCode = executeCommand("decrypt", keyFile.getAbsolutePath());
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());
}
@Test
public void decryptMalformedMessageYieldsBadData() throws IOException {
// Message contains encrypted data packet which contains the plaintext directly - no literal data packet.
// It is therefore malformed.
String malformed = "-----BEGIN PGP MESSAGE-----\n" +
"Version: BCPG v1.72b04\n" +
"\n" +
"hF4D831k4umlLu4SAQdApKA6VDKSLQvwS2kbWqlhcXD8XHdFkSccqv5tBptZnBgw\n" +
"nZNXVhwUpap0ymb4jPTD+EVPKOfPyy04ouIGZAJKkfYDeSL/8sKcbnPPuQJYYjGQ\n" +
"ySDNmidrtTonwcSuwAfRyn74BBqOVhrr8GXkVIfevIlZFQ==\n" +
"=wIgl\n" +
"-----END PGP MESSAGE-----";
File key = writeFile("key.asc", KEY);
pipeStringToStdin(malformed);
int exitCode = executeCommand("decrypt", key.getAbsolutePath());
assertEquals(SOPGPException.BadData.EXIT_CODE, exitCode);
}
@Test
public void decryptWithPasswordWithPendingWhitespaceWorks() throws IOException {
assertEncryptWithPasswordADecryptWithPasswordBWorks("sw0rdf1sh", "sw0rdf1sh \n");
}
@Test
public void encryptWithTrailingWhitespaceDecryptWithoutWorks() throws IOException {
assertEncryptWithPasswordADecryptWithPasswordBWorks("sw0rdf1sh \n", "sw0rdf1sh");
}
@Test
public void decryptWithWhitespacePasswordWorks() throws IOException {
// is encrypted for "sw0rdf1sh \n"
String encryptedToPasswordWithTrailingWhitespace = "-----BEGIN PGP MESSAGE-----\n" +
"\n" +
"jA0ECQMC32tEJug0BCpg0kABfT3dKgA4K8XGpk2ul67BXLZD//fCCSmIQIWnNhE1\n" +
"6q97xFQ628K8f/58+XoBzLqLDT+LEz9Bz+Yg9QfzkEFy\n" +
"=2Y+K\n" +
"-----END PGP MESSAGE-----";
pipeStringToStdin(encryptedToPasswordWithTrailingWhitespace);
File password = writeFile("password", "sw0rdf1sh \n");
ByteArrayOutputStream plaintext = pipeStdoutToStream();
assertSuccess(executeCommand("decrypt", "--with-password", password.getAbsolutePath()));
assertEquals("Hello, World!\n", plaintext.toString());
}
private void assertEncryptWithPasswordADecryptWithPasswordBWorks(String passwordA, String passwordB)
throws IOException {
File passwordAFile = writeFile("password", passwordA);
File passwordBFile = writeFile("passwordWithWS", passwordB);
String msg = "Hello, World!\n";
pipeStringToStdin(msg);
ByteArrayOutputStream ciphertext = pipeStdoutToStream();
assertSuccess(executeCommand("encrypt", "--with-password", passwordAFile.getAbsolutePath()));
pipeStringToStdin(ciphertext.toString());
ByteArrayOutputStream plaintext = pipeStdoutToStream();
assertSuccess(executeCommand("decrypt", "--with-password", passwordBFile.getAbsolutePath()));
assertEquals(msg, plaintext.toString());
}
@Test
public void testDecryptWithoutDecryptionOptionFails() throws IOException {
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);
int exitCode = executeCommand("decrypt");
assertEquals(SOPGPException.MissingArg.EXIT_CODE, exitCode);
}
@Test
public void testEncryptDecryptWithFreshRSAKey() throws IOException {
// Generate key
File passwordFile = writeFile("password", "sw0rdf1sh");
File keyFile = pipeStdoutToFile("key.asc");
assertSuccess(executeCommand("generate-key", "--profile=rfc4880", "--with-key-password", passwordFile.getAbsolutePath(), "Alice <alice@example.org>"));
File certFile = pipeStdoutToFile("cert.asc");
pipeFileToStdin(keyFile);
assertSuccess(executeCommand("extract-cert"));
// Write plaintext
File plaintextFile = writeFile("msg.txt", "Hello, World!\n");
// Encrypt
File ciphertextFile = pipeStdoutToFile("msg.asc");
pipeFileToStdin(plaintextFile);
assertSuccess(executeCommand("encrypt", "--profile=rfc4880", certFile.getAbsolutePath()));
ByteArrayOutputStream decrypted = pipeStdoutToStream();
pipeFileToStdin(ciphertextFile);
assertSuccess(executeCommand("decrypt", "--with-key-password", passwordFile.getAbsolutePath(), keyFile.getAbsolutePath()));
assertEquals("Hello, World!\n", decrypted.toString());
}
}