From 97c8ff8312ba357112e2957d0375df40e8280621 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Sun, 29 Aug 2021 13:35:27 +0200 Subject: [PATCH] Throw WrongConsumingMethodException when processing Cleartext Signed Messages with Inband Signature verification API and vice versa --- .../DecryptionStreamFactory.java | 10 +++++ .../WrongConsumingMethodException.java | 25 +++++++++++ .../ClearsignedMessageUtil.java | 5 ++- .../CleartextSignatureVerificationTest.java | 43 +++++++++++++++++++ .../DetachInbandSignatureAndMessageImpl.java | 8 +++- 5 files changed, 88 insertions(+), 3 deletions(-) create mode 100644 pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java 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 50c183ad..fb900700 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 @@ -58,6 +58,7 @@ import org.pgpainless.exception.MessageNotIntegrityProtectedException; import org.pgpainless.exception.MissingDecryptionMethodException; import org.pgpainless.exception.MissingLiteralDataException; import org.pgpainless.exception.UnacceptableAlgorithmException; +import org.pgpainless.exception.WrongConsumingMethodException; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.key.OpenPgpV4Fingerprint; import org.pgpainless.key.SubkeyIdentifier; @@ -121,6 +122,15 @@ public final class DecryptionStreamFactory { InputStream decoderStream = PGPUtil.getDecoderStream(bufferedIn); decoderStream = CRCingArmoredInputStreamWrapper.possiblyWrap(decoderStream); + if (decoderStream instanceof ArmoredInputStream) { + ArmoredInputStream armor = (ArmoredInputStream) decoderStream; + + if (armor.isClearText()) { + throw new WrongConsumingMethodException("Message appears to be using the Cleartext Signature Framework. " + + "Use PGPainless.verifyCleartextSignedMessage() to verify this message instead."); + } + } + PGPObjectFactory objectFactory = new PGPObjectFactory( decoderStream, keyFingerprintCalculator); diff --git a/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java b/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java new file mode 100644 index 00000000..2ef88330 --- /dev/null +++ b/pgpainless-core/src/main/java/org/pgpainless/exception/WrongConsumingMethodException.java @@ -0,0 +1,25 @@ +/* + * Copyright 2021 Paul Schaub. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.pgpainless.exception; + +import org.bouncycastle.openpgp.PGPException; + +public class WrongConsumingMethodException extends PGPException { + + public WrongConsumingMethodException(String message) { + super(message); + } +} diff --git a/pgpainless-core/src/main/java/org/pgpainless/signature/cleartext_signatures/ClearsignedMessageUtil.java b/pgpainless-core/src/main/java/org/pgpainless/signature/cleartext_signatures/ClearsignedMessageUtil.java index 5fa3bdbe..3c4c4bc5 100644 --- a/pgpainless-core/src/main/java/org/pgpainless/signature/cleartext_signatures/ClearsignedMessageUtil.java +++ b/pgpainless-core/src/main/java/org/pgpainless/signature/cleartext_signatures/ClearsignedMessageUtil.java @@ -25,6 +25,7 @@ import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPSignatureList; import org.bouncycastle.util.Strings; +import org.pgpainless.exception.WrongConsumingMethodException; import org.pgpainless.implementation.ImplementationFactory; import org.pgpainless.util.ArmoredInputStreamFactory; @@ -49,10 +50,10 @@ public final class ClearsignedMessageUtil { */ public static PGPSignatureList detachSignaturesFromInbandClearsignedMessage(InputStream clearsignedInputStream, OutputStream messageOutputStream) - throws IOException { + throws IOException, WrongConsumingMethodException { ArmoredInputStream in = ArmoredInputStreamFactory.get(clearsignedInputStream); if (!in.isClearText()) { - throw new IOException("Message is not clearsigned."); + throw new WrongConsumingMethodException("Message is not using the Cleartext Signature Framework."); } OutputStream out = new BufferedOutputStream(messageOutputStream); 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 db0ac8ea..c09d3d98 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 @@ -17,6 +17,7 @@ package org.pgpainless.decryption_verification; import static org.junit.jupiter.api.Assertions.assertArrayEquals; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; @@ -32,6 +33,7 @@ import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.Test; import org.pgpainless.PGPainless; +import org.pgpainless.exception.WrongConsumingMethodException; import org.pgpainless.key.TestKeys; import org.pgpainless.signature.CertificateValidator; import org.pgpainless.signature.SignatureUtils; @@ -157,4 +159,45 @@ public class CleartextSignatureVerificationTest { OpenPgpMetadata metadata = decryptionStream.getResult(); 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" + + "Version: PGPainless\n" + + "\n" + + "kA0DAQoTVzbmkxrPNwwBy8BJYgAAAAAAQWgsIEp1bGlldCwgaWYgdGhlIG1lYXN1\n" + + "cmUgb2YgdGh5IGpveQpCZSBoZWFwZWQgbGlrZSBtaW5lLCBhbmQgdGhhdCB0aHkg\n" + + "c2tpbGwgYmUgbW9yZQpUbyBibGF6b24gaXQsIHRoZW4gc3dlZXRlbiB3aXRoIHRo\n" + + "eSBicmVhdGgKVGhpcyBuZWlnaGJvciBhaXIsIGFuZCBsZXQgcmljaCBtdXNpY+KA\n" + + "mXMgdG9uZ3VlClVuZm9sZCB0aGUgaW1hZ2luZWQgaGFwcGluZXNzIHRoYXQgYm90\n" + + "aApSZWNlaXZlIGluIGVpdGhlciBieSB0aGlzIGRlYXIgZW5jb3VudGVyLoh1BAET\n" + + "CgAGBQJhK2q9ACEJEFc25pMazzcMFiEET2ZcTcLEZgvGQl5BVzbmkxrPNwxr8gD+\n" + + "MDfg+qccpsoJVgHIW8mRPBQowXDyw+oNHsf28ii+/pEBAO/RXhFkZBPzlfDJMJVT\n" + + "UwJJeuna1R4yOoWjq0zqRvrg\n" + + "=dBiV\n" + + "-----END PGP MESSAGE-----\n"; + + PGPPublicKeyRing certificate = TestKeys.getEmilPublicKeyRing(); + ConsumerOptions options = new ConsumerOptions() + .addVerificationCert(certificate); + + assertThrows(WrongConsumingMethodException.class, () -> + PGPainless.verifyCleartextSignedMessage() + .onInputStream(new ByteArrayInputStream(inlineSignedMessage.getBytes(StandardCharsets.UTF_8))) + .withStrategy(new InMemoryMultiPassStrategy()) + .withOptions(options) + .process()); + } } diff --git a/pgpainless-sop/src/main/java/org/pgpainless/sop/DetachInbandSignatureAndMessageImpl.java b/pgpainless-sop/src/main/java/org/pgpainless/sop/DetachInbandSignatureAndMessageImpl.java index 418c9c36..b904285d 100644 --- a/pgpainless-sop/src/main/java/org/pgpainless/sop/DetachInbandSignatureAndMessageImpl.java +++ b/pgpainless-sop/src/main/java/org/pgpainless/sop/DetachInbandSignatureAndMessageImpl.java @@ -22,6 +22,7 @@ import java.io.OutputStream; import org.bouncycastle.bcpg.ArmoredOutputStream; import org.bouncycastle.openpgp.PGPSignature; import org.bouncycastle.openpgp.PGPSignatureList; +import org.pgpainless.exception.WrongConsumingMethodException; import org.pgpainless.signature.cleartext_signatures.ClearsignedMessageUtil; import org.pgpainless.util.ArmoredOutputStreamFactory; import sop.ReadyWithResult; @@ -49,7 +50,12 @@ public class DetachInbandSignatureAndMessageImpl implements DetachInbandSignatur return new Signatures() { @Override public void writeTo(OutputStream signatureOutputStream) throws IOException { - PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(messageInputStream, messageOutputStream); + PGPSignatureList signatures = null; + try { + signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(messageInputStream, messageOutputStream); + } catch (WrongConsumingMethodException e) { + throw new IOException(e); + } if (armor) { ArmoredOutputStream armorOut = ArmoredOutputStreamFactory.get(signatureOutputStream); for (PGPSignature signature : signatures) {