diff --git a/external-sop/src/main/java/sop/external/ExternalSOP.java b/external-sop/src/main/java/sop/external/ExternalSOP.java index 30154cd..c3ecd00 100644 --- a/external-sop/src/main/java/sop/external/ExternalSOP.java +++ b/external-sop/src/main/java/sop/external/ExternalSOP.java @@ -274,6 +274,7 @@ public class ExternalSOP implements SOP { outputStream.write(buf, 0, r); } + outputStream.flush(); outputStream.close(); ExternalSOP.finish(process); } @@ -299,19 +300,17 @@ public class ExternalSOP implements SOP { while ((r = standardIn.read(buf)) > 0) { processOut.write(buf, 0, r); } - standardIn.close(); - try { - processOut.close(); - } catch (IOException e) { - // ignore - } + + processOut.flush(); + processOut.close(); while ((r = processIn.read(buf)) > 0) { outputStream.write(buf, 0 , r); } - processIn.close(); + + outputStream.flush(); outputStream.close(); finish(process); diff --git a/external-sop/src/test/java/sop/external/ExternalArmorDearmorRoundTripTest.java b/external-sop/src/test/java/sop/external/ExternalArmorDearmorRoundTripTest.java index 7763842..4079fd0 100644 --- a/external-sop/src/test/java/sop/external/ExternalArmorDearmorRoundTripTest.java +++ b/external-sop/src/test/java/sop/external/ExternalArmorDearmorRoundTripTest.java @@ -10,9 +10,11 @@ import org.junit.jupiter.api.condition.EnabledIf; import java.io.IOException; import java.nio.charset.StandardCharsets; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static sop.external.JUtils.arrayStartsWith; import static sop.external.JUtils.assertArrayStartsWith; +import static sop.external.JUtils.assertAsciiArmorEquals; @EnabledIf("sop.external.AbstractExternalSOPTest#isExternalSopInstalled") public class ExternalArmorDearmorRoundTripTest extends AbstractExternalSOPTest { @@ -21,6 +23,10 @@ public class ExternalArmorDearmorRoundTripTest extends AbstractExternalSOPTest { private static final byte[] BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES = BEGIN_PGP_PRIVATE_KEY_BLOCK.getBytes(StandardCharsets.UTF_8); private static final String BEGIN_PGP_PUBLIC_KEY_BLOCK = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n"; private static final byte[] BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES = BEGIN_PGP_PUBLIC_KEY_BLOCK.getBytes(StandardCharsets.UTF_8); + private static final String BEGIN_PGP_MESSAGE = "-----BEGIN PGP MESSAGE-----\n"; + private static final byte[] BEGIN_PGP_MESSAGE_BYTES = BEGIN_PGP_MESSAGE.getBytes(StandardCharsets.UTF_8); + private static final String BEGIN_PGP_SIGNATURE = "-----BEGIN PGP SIGNATURE-----\n"; + private static final byte[] BEGIN_PGP_SIGNATURE_BYTES = BEGIN_PGP_SIGNATURE.getBytes(StandardCharsets.UTF_8); @Test public void dearmorArmorAliceKey() throws IOException { @@ -37,9 +43,9 @@ public class ExternalArmorDearmorRoundTripTest extends AbstractExternalSOPTest { .getBytes(); assertArrayStartsWith(armored, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(aliceKey, armored); } - @Test public void dearmorArmorAliceCert() throws IOException { byte[] aliceCert = TestKeys.ALICE_CERT.getBytes(StandardCharsets.UTF_8); @@ -55,5 +61,162 @@ public class ExternalArmorDearmorRoundTripTest extends AbstractExternalSOPTest { .getBytes(); assertArrayStartsWith(armored, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(aliceCert, armored); } + + @Test + public void dearmorArmorBobKey() throws IOException { + byte[] bobKey = TestKeys.BOB_KEY.getBytes(StandardCharsets.UTF_8); + + byte[] dearmored = getSop().dearmor() + .data(bobKey) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(bobKey, armored); + } + + @Test + public void dearmorArmorBobCert() throws IOException { + byte[] bobCert = TestKeys.BOB_CERT.getBytes(StandardCharsets.UTF_8); + + byte[] dearmored = getSop().dearmor() + .data(bobCert) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(bobCert, armored); + } + + @Test + public void dearmorArmorCarolKey() throws IOException { + byte[] carolKey = TestKeys.CAROL_KEY.getBytes(StandardCharsets.UTF_8); + + byte[] dearmored = getSop().dearmor() + .data(carolKey) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_PRIVATE_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(carolKey, armored); + } + + @Test + public void dearmorArmorCarolCert() throws IOException { + byte[] carolCert = TestKeys.CAROL_CERT.getBytes(StandardCharsets.UTF_8); + + byte[] dearmored = getSop().dearmor() + .data(carolCert) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_PUBLIC_KEY_BLOCK_BYTES); + assertAsciiArmorEquals(carolCert, armored); + } + + @Test + public void dearmorArmorMessage() throws IOException { + ignoreIf("sqop", Is.leq, "0.26.1"); // falsely reports Invalid Data Type + byte[] message = ("-----BEGIN PGP MESSAGE-----\n" + + "\n" + + "wV4DR2b2udXyHrYSAQdAMZy9Iqb1IxszjI3v+TsfK//0lnJ9PKHDqVAB5ohp+RMw\n" + + "8fmuL3phS9uISFT/DrizC8ALJhMqw5R+lLB/RvTTA/qS6tN5dRyL+YLFU3/N0CRF\n" + + "0j8BtQEsMmRo60LzUq/OBI0dFjwFq1efpfOGkpRYkuIzndCjBEgnLUkrHzUc1uD9\n" + + "CePQFpprprnGEzpE3flQLUc=\n" + + "=ZiFR\n" + + "-----END PGP MESSAGE-----\n").getBytes(StandardCharsets.UTF_8); + byte[] dearmored = getSop().dearmor() + .data(message) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_MESSAGE_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_MESSAGE_BYTES); + assertAsciiArmorEquals(message, armored); + } + + @Test + public void dearmorArmorSignature() throws IOException { + byte[] signature = ("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wr0EABYKAG8FgmPBdRAJEPIxVQxPR+OORxQAAAAAAB4AIHNhbHRAbm90YXRpb25z\n" + + "LnNlcXVvaWEtcGdwLm9yZ2un17fF3C46Adgzp0mU4RG8Txy/T/zOBcBw/NYaLGrQ\n" + + "FiEE64W7X6M6deFelE5j8jFVDE9H444AAMiEAP9LBQWLo4oP5IrFZPuSUQSPsUxB\n" + + "c+Qu1raXDKzS/8Q9IAD+LnHIjRHcqNPobNHXF/saXIYXeZR+LJKszTJozzwqdQE=\n" + + "=GHvQ\n" + + "-----END PGP SIGNATURE-----\n").getBytes(StandardCharsets.UTF_8); + + byte[] dearmored = getSop().dearmor() + .data(signature) + .getBytes(); + + assertFalse(arrayStartsWith(dearmored, BEGIN_PGP_SIGNATURE_BYTES)); + + byte[] armored = getSop().armor() + .data(dearmored) + .getBytes(); + + assertArrayStartsWith(armored, BEGIN_PGP_SIGNATURE_BYTES); + assertAsciiArmorEquals(signature, armored); + } + + @Test + public void testDearmoringTwiceIsIdempotent() throws IOException { + ignoreIf("sqop", Is.eq, "0.27.2"); // IO error because: EOF + + byte[] dearmored = getSop().dearmor() + .data(TestKeys.ALICE_KEY.getBytes(StandardCharsets.UTF_8)) + .getBytes(); + + byte[] dearmoredAgain = getSop().dearmor() + .data(dearmored) + .getBytes(); + + assertArrayEquals(dearmored, dearmoredAgain); + } + + @Test + public void testArmoringTwiceIsIdempotent() throws IOException { + byte[] armored = ("-----BEGIN PGP SIGNATURE-----\n" + + "\n" + + "wr0EABYKAG8FgmPBdRAJEPIxVQxPR+OORxQAAAAAAB4AIHNhbHRAbm90YXRpb25z\n" + + "LnNlcXVvaWEtcGdwLm9yZ2un17fF3C46Adgzp0mU4RG8Txy/T/zOBcBw/NYaLGrQ\n" + + "FiEE64W7X6M6deFelE5j8jFVDE9H444AAMiEAP9LBQWLo4oP5IrFZPuSUQSPsUxB\n" + + "c+Qu1raXDKzS/8Q9IAD+LnHIjRHcqNPobNHXF/saXIYXeZR+LJKszTJozzwqdQE=\n" + + "=GHvQ\n" + + "-----END PGP SIGNATURE-----\n").getBytes(StandardCharsets.UTF_8); + + byte[] armoredAgain = getSop().armor() + .data(armored) + .getBytes(); + + assertAsciiArmorEquals(armored, armoredAgain); + } + } diff --git a/external-sop/src/test/java/sop/external/JUtils.java b/external-sop/src/test/java/sop/external/JUtils.java index 36d6d85..d0d1601 100644 --- a/external-sop/src/test/java/sop/external/JUtils.java +++ b/external-sop/src/test/java/sop/external/JUtils.java @@ -4,8 +4,10 @@ package sop.external; +import java.nio.charset.StandardCharsets; import java.util.Arrays; +import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.fail; public class JUtils { @@ -50,4 +52,21 @@ public class JUtils { "Actual: <" + Arrays.toString(actual) + ">"); } } + + public static void assertAsciiArmorEquals(byte[] first, byte[] second) { + byte[] firstCleaned = removeArmorHeaders(first); + byte[] secondCleaned = removeArmorHeaders(second); + + assertArrayEquals(firstCleaned, secondCleaned); + } + + public static byte[] removeArmorHeaders(byte[] armor) { + String string = new String(armor, StandardCharsets.UTF_8); + string = string.replaceAll("Comment: .+\\R", "") + .replaceAll("Version: .+\\R", "") + .replaceAll("MessageID: .+\\R", "") + .replaceAll("Hash: .+\\R", "") + .replaceAll("Charset: .+\\R", ""); + return string.getBytes(StandardCharsets.UTF_8); + } }