diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java index 1a38fced..990e628d 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.java @@ -343,7 +343,8 @@ public class OpenPgpMessageInputStream extends InputStream { return null; } - private PGPOnePassSignatureList readOnePassSignatures() throws IOException { + private PGPOnePassSignatureListWrapper readOnePassSignatures() throws IOException { + List encapsulating = new ArrayList<>(); ByteArrayOutputStream buf = new ByteArrayOutputStream(); BCPGOutputStream bcpgOut = new BCPGOutputStream(buf); int tag; @@ -351,14 +352,16 @@ public class OpenPgpMessageInputStream extends InputStream { Packet packet = bcpgIn.readPacket(); if (tag == PacketTags.ONE_PASS_SIGNATURE) { OnePassSignaturePacket sigPacket = (OnePassSignaturePacket) packet; - sigPacket.encode(bcpgOut); + byte[] bytes = sigPacket.getEncoded(); + encapsulating.add(bytes[bytes.length - 1] == 1); + bcpgOut.write(bytes); } } bcpgOut.close(); PGPObjectFactory objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(buf.toByteArray()); PGPOnePassSignatureList signatureList = (PGPOnePassSignatureList) objectFactory.nextObject(); - return signatureList; + return new PGPOnePassSignatureListWrapper(signatureList, encapsulating); } private PGPSignatureList readSignatures() throws IOException { @@ -490,6 +493,26 @@ public class OpenPgpMessageInputStream extends InputStream { } } + /** + * Workaround for BC not exposing, whether an OPS is encapsulating or not. + * TODO: Remove once our PR is merged + * + * @see PR against BC + */ + private static class PGPOnePassSignatureListWrapper { + private final PGPOnePassSignatureList list; + private final List encapsulating; + + public PGPOnePassSignatureListWrapper(PGPOnePassSignatureList signatures, List encapsulating) { + this.list = signatures; + this.encapsulating = encapsulating; + } + + public int size() { + return list.size(); + } + } + private static class Signatures { final ConsumerOptions options; List detachedSignatures = new ArrayList<>(); @@ -521,9 +544,9 @@ public class OpenPgpMessageInputStream extends InputStream { } } - void addOnePassSignatures(PGPOnePassSignatureList signatures) { + void addOnePassSignatures(PGPOnePassSignatureListWrapper signatures) { System.out.println("Adding " + signatures.size() + " OPSs"); - for (PGPOnePassSignature ops : signatures) { + for (PGPOnePassSignature ops : signatures.list) { PGPPublicKeyRing certificate = findCertificate(ops.getKeyID()); initialize(ops, certificate); this.onePassSignatures.add(ops); diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java new file mode 100644 index 00000000..ab24f22e --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/TeeBCPGInputStream.java @@ -0,0 +1,35 @@ +package org.pgpainless.decryption_verification; + +import org.bouncycastle.bcpg.BCPGInputStream; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +public class TeeBCPGInputStream extends BCPGInputStream { + + private final OutputStream out; + + public TeeBCPGInputStream(InputStream in, OutputStream outputStream) { + super(in); + this.out = outputStream; + } + + @Override + public int read() throws IOException { + int r = super.read(); + if (r != -1) { + out.write(r); + } + return r; + } + + @Override + public int read(byte[] buf, int off, int len) throws IOException { + int r = super.read(buf, off, len); + if (r > 0) { + out.write(buf, off, r); + } + return r; + } +} diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TeeBCPGInputStreamTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TeeBCPGInputStreamTest.java new file mode 100644 index 00000000..765221d3 --- /dev/null +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/TeeBCPGInputStreamTest.java @@ -0,0 +1,58 @@ +package org.pgpainless.decryption_verification; + +import org.bouncycastle.bcpg.ArmoredInputStream; +import org.bouncycastle.bcpg.ArmoredOutputStream; +import org.bouncycastle.bcpg.BCPGInputStream; +import org.bouncycastle.bcpg.Packet; +import org.bouncycastle.openpgp.PGPCompressedData; +import org.bouncycastle.openpgp.PGPException; +import org.junit.jupiter.api.Test; +import org.pgpainless.algorithm.OpenPgpPacket; +import org.pgpainless.util.ArmoredInputStreamFactory; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class TeeBCPGInputStreamTest { + + private static final String INBAND_SIGNED = "-----BEGIN PGP MESSAGE-----\n" + + "Version: PGPainless\n" + + "\n" + + "owGbwMvMyCUWdXSHvVTUtXbG0yJJDCDgkZqTk6+jEJ5flJOiyNVRysIoxsXAxsqU\n" + + "GDiVjUGRUwCmQUyRRWnOn9Z/PIseF3Yz6cCEL05nZDj1OClo75WVTjNmJPemW6qV\n" + + "6ki//1K1++2s0qTP+0N11O4z/BVLDDdxnmQryS+5VXjBX7/0Hxnm/eqeX6Zum35r\n" + + "M8e7ufwA\n" + + "=RDiy\n" + + "-----END PGP MESSAGE-----"; + + @Test + public void test() throws IOException, PGPException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ArmoredOutputStream armorOut = new ArmoredOutputStream(out); + + ByteArrayInputStream bytesIn = new ByteArrayInputStream(INBAND_SIGNED.getBytes(StandardCharsets.UTF_8)); + ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn); + BCPGInputStream bcpgIn = new BCPGInputStream(armorIn); + TeeBCPGInputStream teeIn = new TeeBCPGInputStream(bcpgIn, armorOut); + + ByteArrayOutputStream nestedOut = new ByteArrayOutputStream(); + ArmoredOutputStream nestedArmorOut = new ArmoredOutputStream(nestedOut); + + PGPCompressedData compressedData = new PGPCompressedData(teeIn); + InputStream nestedStream = compressedData.getDataStream(); + BCPGInputStream nestedBcpgIn = new BCPGInputStream(nestedStream); + TeeBCPGInputStream nestedTeeIn = new TeeBCPGInputStream(nestedBcpgIn, nestedArmorOut); + + int tag; + while ((tag = nestedTeeIn.nextPacketTag()) != -1) { + System.out.println(OpenPgpPacket.requireFromTag(tag)); + Packet packet = nestedTeeIn.readPacket(); + } + + nestedArmorOut.close(); + System.out.println(nestedOut); + } +}