diff --git a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java
index 7d5ca4b0..5bbe0e14 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/PGPainless.java
@@ -22,8 +22,6 @@ import org.pgpainless.key.modification.secretkeyring.SecretKeyRingEditorInterfac
import org.pgpainless.key.parsing.KeyRingReader;
import org.pgpainless.key.util.KeyRingUtils;
import org.pgpainless.policy.Policy;
-import org.pgpainless.decryption_verification.cleartext_signatures.VerifyCleartextSignatures;
-import org.pgpainless.decryption_verification.cleartext_signatures.VerifyCleartextSignaturesImpl;
import org.pgpainless.util.ArmorUtils;
public final class PGPainless {
@@ -91,26 +89,6 @@ public final class PGPainless {
return new DecryptionBuilder();
}
- /**
- * Verify a cleartext-signed message.
- * Cleartext signed messages are often found in emails and look like this:
- *
- * {@code
- * -----BEGIN PGP SIGNED MESSAGE-----
- * Hash: [Hash algorithm]
- * [Human Readable Message Body]
- * -----BEGIN PGP SIGNATURE-----
- * [Signature]
- * -----END PGP SIGNATURE-----
- * }
- *
- *
- * @return builder
- */
- public static VerifyCleartextSignatures verifyCleartextSignedMessage() {
- return new VerifyCleartextSignaturesImpl();
- }
-
/**
* Make changes to a key ring.
* This method can be used to change key expiration dates and passphrases, or add/remove/revoke subkeys.
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java
index 3c1a9454..1884ee92 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/ConsumerOptions.java
@@ -22,6 +22,8 @@ import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.PGPSecretKeyRingCollection;
import org.bouncycastle.openpgp.PGPSignature;
+import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy;
+import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy;
import org.pgpainless.exception.NotYetImplementedException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
import org.pgpainless.signature.SignatureUtils;
@@ -50,6 +52,7 @@ public class ConsumerOptions {
private final Set decryptionPassphrases = new HashSet<>();
private MissingKeyPassphraseStrategy missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE;
+ private MultiPassStrategy multiPassStrategy = new InMemoryMultiPassStrategy();
/**
* Consider signatures on the message made before the given timestamp invalid.
@@ -327,4 +330,26 @@ public class ConsumerOptions {
MissingKeyPassphraseStrategy getMissingKeyPassphraseStrategy() {
return missingKeyPassphraseStrategy;
}
+
+ /**
+ * Set a custom multi-pass strategy for processing cleartext-signed messages.
+ * Uses {@link InMemoryMultiPassStrategy} by default.
+ *
+ * @param multiPassStrategy multi-pass caching strategy
+ * @return builder
+ */
+ public ConsumerOptions setMultiPassStrategy(@Nonnull MultiPassStrategy multiPassStrategy) {
+ this.multiPassStrategy = multiPassStrategy;
+ return this;
+ }
+
+ /**
+ * Return the currently configured {@link MultiPassStrategy}.
+ * Defaults to {@link InMemoryMultiPassStrategy}.
+ *
+ * @return multi-pass strategy
+ */
+ public MultiPassStrategy getMultiPassStrategy() {
+ return multiPassStrategy;
+ }
}
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java
index 6dcc355b..0611ae99 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionBuilder.java
@@ -4,31 +4,48 @@
package org.pgpainless.decryption_verification;
+import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import javax.annotation.Nonnull;
import org.bouncycastle.openpgp.PGPException;
+import org.pgpainless.decryption_verification.cleartext_signatures.VerifyCleartextSignaturesImpl;
+import org.pgpainless.exception.WrongConsumingMethodException;
public class DecryptionBuilder implements DecryptionBuilderInterface {
- private InputStream inputStream;
+ public static int BUFFER_SIZE = 4096;
@Override
public DecryptWith onInputStream(@Nonnull InputStream inputStream) {
- this.inputStream = inputStream;
- return new DecryptWithImpl();
+ return new DecryptWithImpl(inputStream);
}
class DecryptWithImpl implements DecryptWith {
+ private BufferedInputStream inputStream;
+
+ DecryptWithImpl(InputStream inputStream) {
+ this.inputStream = new BufferedInputStream(inputStream, BUFFER_SIZE);
+ this.inputStream.mark(BUFFER_SIZE);
+ }
+
@Override
public DecryptionStream withOptions(ConsumerOptions consumerOptions) throws PGPException, IOException {
if (consumerOptions == null) {
throw new IllegalArgumentException("Consumer options cannot be null.");
}
- return DecryptionStreamFactory.create(inputStream, consumerOptions);
+ try {
+ return DecryptionStreamFactory.create(inputStream, consumerOptions);
+ } catch (WrongConsumingMethodException e) {
+ inputStream.reset();
+ return new VerifyCleartextSignaturesImpl()
+ .onInputStream(inputStream)
+ .withOptions(consumerOptions)
+ .getVerificationStream();
+ }
}
}
}
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java
index ad7088f1..66ed2d05 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/DecryptionStreamFactory.java
@@ -121,7 +121,8 @@ public final class DecryptionStreamFactory {
private DecryptionStream parseOpenPGPDataAndCreateDecryptionStream(InputStream inputStream) throws IOException, PGPException {
// Make sure we handle armored and non-armored data properly
- BufferedInputStream bufferedIn = new BufferedInputStream(inputStream);
+ BufferedInputStream bufferedIn = new BufferedInputStream(inputStream, 512);
+ bufferedIn.mark(512);
InputStream decoderStream;
PGPObjectFactory objectFactory;
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/CleartextSignatureProcessor.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/CleartextSignatureProcessor.java
index 636b78e4..87facea7 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/CleartextSignatureProcessor.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/CleartextSignatureProcessor.java
@@ -31,11 +31,9 @@ public class CleartextSignatureProcessor {
private final ArmoredInputStream in;
private final ConsumerOptions options;
- private final MultiPassStrategy multiPassStrategy;
public CleartextSignatureProcessor(InputStream inputStream,
- ConsumerOptions options,
- MultiPassStrategy multiPassStrategy)
+ ConsumerOptions options)
throws IOException {
if (inputStream instanceof ArmoredInputStream) {
this.in = (ArmoredInputStream) inputStream;
@@ -43,7 +41,6 @@ public class CleartextSignatureProcessor {
this.in = ArmoredInputStreamFactory.get(inputStream);
}
this.options = options;
- this.multiPassStrategy = multiPassStrategy;
}
/**
@@ -67,6 +64,7 @@ public class CleartextSignatureProcessor {
.setSymmetricKeyAlgorithm(SymmetricKeyAlgorithm.NULL)
.setFileEncoding(StreamEncoding.TEXT);
+ MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(in, multiPassStrategy.getMessageOutputStream());
for (PGPSignature signature : signatures) {
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignatures.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignatures.java
index 31317c6c..52360869 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignatures.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignatures.java
@@ -4,7 +4,6 @@
package org.pgpainless.decryption_verification.cleartext_signatures;
-import java.io.File;
import java.io.IOException;
import java.io.InputStream;
@@ -20,23 +19,7 @@ public interface VerifyCleartextSignatures {
* @param inputStream inputstream
* @return api handle
*/
- WithStrategy onInputStream(InputStream inputStream);
-
- interface WithStrategy {
-
- /**
- * Provide a {@link MultiPassStrategy} which is used to store the message content.
- * Since cleartext-signed messages cannot be processed in one pass, the message has to be passed twice.
- * Therefore the user needs to decide upon a strategy where to cache/store the message between the passes.
- * This could be {@link MultiPassStrategy#writeMessageToFile(File)} or {@link MultiPassStrategy#keepMessageInMemory()},
- * depending on message size and use-case.
- *
- * @param multiPassStrategy strategy
- * @return api handle
- */
- VerifyWith withStrategy(MultiPassStrategy multiPassStrategy);
-
- }
+ VerifyWith onInputStream(InputStream inputStream);
interface VerifyWith {
diff --git a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignaturesImpl.java b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignaturesImpl.java
index 276e027f..fde90874 100644
--- a/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignaturesImpl.java
+++ b/pgpainless-core/src/main/java/org/pgpainless/decryption_verification/cleartext_signatures/VerifyCleartextSignaturesImpl.java
@@ -12,31 +12,18 @@ import org.pgpainless.decryption_verification.ConsumerOptions;
public class VerifyCleartextSignaturesImpl implements VerifyCleartextSignatures {
private InputStream inputStream;
- private MultiPassStrategy multiPassStrategy;
@Override
- public WithStrategy onInputStream(InputStream inputStream) {
+ public VerifyWithImpl onInputStream(InputStream inputStream) {
VerifyCleartextSignaturesImpl.this.inputStream = inputStream;
- return new WithStrategyImpl();
- }
-
- public class WithStrategyImpl implements WithStrategy {
-
- @Override
- public VerifyWith withStrategy(MultiPassStrategy multiPassStrategy) {
- if (multiPassStrategy == null) {
- throw new NullPointerException("MultiPassStrategy cannot be null.");
- }
- VerifyCleartextSignaturesImpl.this.multiPassStrategy = multiPassStrategy;
- return new VerifyWithImpl();
- }
+ return new VerifyWithImpl();
}
public class VerifyWithImpl implements VerifyWith {
@Override
public CleartextSignatureProcessor withOptions(ConsumerOptions options) throws IOException {
- return new CleartextSignatureProcessor(inputStream, options, multiPassStrategy);
+ return new CleartextSignatureProcessor(inputStream, options);
}
}
diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java
index c355438d..cdba4d07 100644
--- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java
+++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CleartextSignatureVerificationTest.java
@@ -25,9 +25,9 @@ import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.DocumentSignatureType;
-import org.pgpainless.decryption_verification.cleartext_signatures.CleartextSignatureProcessor;
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy;
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy;
+import org.pgpainless.decryption_verification.cleartext_signatures.VerifyCleartextSignaturesImpl;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
@@ -79,12 +79,11 @@ public class CleartextSignatureVerificationTest {
.addVerificationCert(signingKeys);
InMemoryMultiPassStrategy multiPassStrategy = MultiPassStrategy.keepMessageInMemory();
- CleartextSignatureProcessor processor = PGPainless.verifyCleartextSignedMessage()
+ options.setMultiPassStrategy(multiPassStrategy);
+ DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(new ByteArrayInputStream(MESSAGE_SIGNED))
- .withStrategy(multiPassStrategy)
.withOptions(options);
- DecryptionStream decryptionStream = processor.getVerificationStream();
ByteArrayOutputStream out = new ByteArrayOutputStream();
Streams.pipeAll(decryptionStream, out);
decryptionStream.close();
@@ -107,13 +106,11 @@ public class CleartextSignatureVerificationTest {
File tempDir = TestUtils.createTempDirectory();
File file = new File(tempDir, "file");
MultiPassStrategy multiPassStrategy = MultiPassStrategy.writeMessageToFile(file);
- CleartextSignatureProcessor processor = PGPainless.verifyCleartextSignedMessage()
+ options.setMultiPassStrategy(multiPassStrategy);
+ DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(new ByteArrayInputStream(MESSAGE_SIGNED))
- .withStrategy(multiPassStrategy)
.withOptions(options);
- DecryptionStream decryptionStream = processor.getVerificationStream();
-
ByteArrayOutputStream out = new ByteArrayOutputStream();
Streams.pipeAll(decryptionStream, out);
decryptionStream.close();
@@ -173,18 +170,6 @@ public class CleartextSignatureVerificationTest {
assertEquals(1, metadata.getVerifiedSignatures().size());
}
- @Test
- public void consumingCleartextSignedMessageWithNormalAPIThrowsWrongConsumingMethodException() throws IOException, PGPException {
- PGPPublicKeyRing certificate = TestKeys.getEmilPublicKeyRing();
- ConsumerOptions options = new ConsumerOptions()
- .addVerificationCert(certificate);
-
- assertThrows(WrongConsumingMethodException.class, () ->
- PGPainless.decryptAndOrVerify()
- .onInputStream(new ByteArrayInputStream(MESSAGE_SIGNED))
- .withOptions(options));
- }
-
@Test
public void consumingInlineSignedMessageWithCleartextSignedVerificationApiThrowsWrongConsumingMethodException() throws PGPException, IOException {
String inlineSignedMessage = "-----BEGIN PGP MESSAGE-----\n" +
@@ -207,11 +192,10 @@ public class CleartextSignatureVerificationTest {
.addVerificationCert(certificate);
assertThrows(WrongConsumingMethodException.class, () ->
- PGPainless.verifyCleartextSignedMessage()
- .onInputStream(new ByteArrayInputStream(inlineSignedMessage.getBytes(StandardCharsets.UTF_8)))
- .withStrategy(new InMemoryMultiPassStrategy())
- .withOptions(options)
- .getVerificationStream());
+ new VerifyCleartextSignaturesImpl()
+ .onInputStream(new ByteArrayInputStream(inlineSignedMessage.getBytes(StandardCharsets.UTF_8)))
+ .withOptions(options)
+ .getVerificationStream());
}
@Test
@@ -223,7 +207,7 @@ public class CleartextSignatureVerificationTest {
ByteArrayOutputStream signedOut = new ByteArrayOutputStream();
EncryptionStream signingStream = PGPainless.encryptAndOrSign().onOutputStream(signedOut)
.withOptions(ProducerOptions.sign(SigningOptions.get()
- .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKey, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT))
+ .addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(), secretKey, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT))
.setCleartextSigned());
Streams.pipeAll(msgIn, signingStream);
@@ -232,12 +216,10 @@ public class CleartextSignatureVerificationTest {
String signed = signedOut.toString();
ByteArrayInputStream signedIn = new ByteArrayInputStream(signed.getBytes(StandardCharsets.UTF_8));
- DecryptionStream verificationStream = PGPainless.verifyCleartextSignedMessage()
+ DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
.onInputStream(signedIn)
- .withStrategy(new InMemoryMultiPassStrategy())
.withOptions(new ConsumerOptions()
- .addVerificationCert(TestKeys.getEmilPublicKeyRing()))
- .getVerificationStream();
+ .addVerificationCert(TestKeys.getEmilPublicKeyRing()));
ByteArrayOutputStream msgOut = new ByteArrayOutputStream();
Streams.pipeAll(verificationStream, msgOut);
diff --git a/pgpainless-core/src/test/java/org/pgpainless/example/DecryptOrVerify.java b/pgpainless-core/src/test/java/org/pgpainless/example/DecryptOrVerify.java
index da641325..d0f3461c 100644
--- a/pgpainless-core/src/test/java/org/pgpainless/example/DecryptOrVerify.java
+++ b/pgpainless-core/src/test/java/org/pgpainless/example/DecryptOrVerify.java
@@ -7,7 +7,6 @@ package org.pgpainless.example;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.io.BufferedInputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
@@ -24,11 +23,9 @@ import org.pgpainless.algorithm.DocumentSignatureType;
import org.pgpainless.decryption_verification.ConsumerOptions;
import org.pgpainless.decryption_verification.DecryptionStream;
import org.pgpainless.decryption_verification.OpenPgpMetadata;
-import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy;
import org.pgpainless.encryption_signing.EncryptionStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import org.pgpainless.encryption_signing.SigningOptions;
-import org.pgpainless.exception.WrongConsumingMethodException;
import org.pgpainless.key.protection.SecretKeyRingProtector;
public class DecryptOrVerify {
@@ -97,22 +94,10 @@ public class DecryptOrVerify {
for (String signed : new String[] {INBAND_SIGNED, CLEARTEXT_SIGNED}) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(signed.getBytes(StandardCharsets.UTF_8));
- BufferedInputStream bufIn = new BufferedInputStream(in);
- bufIn.mark(512);
DecryptionStream verificationStream;
- try {
verificationStream = PGPainless.decryptAndOrVerify()
- .onInputStream(bufIn)
+ .onInputStream(in)
.withOptions(options);
- } catch (WrongConsumingMethodException e) {
- bufIn.reset();
- // Cleartext Signed Message
- verificationStream = PGPainless.verifyCleartextSignedMessage()
- .onInputStream(bufIn)
- .withStrategy(new InMemoryMultiPassStrategy())
- .withOptions(options)
- .getVerificationStream();
- }
Streams.pipeAll(verificationStream, out);
verificationStream.close();
@@ -140,11 +125,9 @@ public class DecryptOrVerify {
ByteArrayInputStream signedIn = new ByteArrayInputStream(out.toByteArray());
- DecryptionStream verificationStream = PGPainless.verifyCleartextSignedMessage()
+ DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
.onInputStream(signedIn)
- .withStrategy(new InMemoryMultiPassStrategy())
- .withOptions(new ConsumerOptions().addVerificationCert(certificate))
- .getVerificationStream();
+ .withOptions(new ConsumerOptions().addVerificationCert(certificate));
ByteArrayOutputStream plain = new ByteArrayOutputStream();
Streams.pipeAll(verificationStream, plain);