1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-23 11:27:57 +01:00

Fix unvalid cursor mark for large cleartext signed messages

Fixes #219, #220
This commit is contained in:
Paul Schaub 2021-11-24 14:51:16 +01:00
parent 50f565dd8c
commit 16e283f3a6
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
5 changed files with 88 additions and 5 deletions

View file

@ -53,6 +53,7 @@ public class ConsumerOptions {
private MissingKeyPassphraseStrategy missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE; private MissingKeyPassphraseStrategy missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE;
private MultiPassStrategy multiPassStrategy = new InMemoryMultiPassStrategy(); private MultiPassStrategy multiPassStrategy = new InMemoryMultiPassStrategy();
private boolean cleartextSigned;
/** /**
* Consider signatures on the message made before the given timestamp invalid. * Consider signatures on the message made before the given timestamp invalid.
@ -352,4 +353,20 @@ public class ConsumerOptions {
public MultiPassStrategy getMultiPassStrategy() { public MultiPassStrategy getMultiPassStrategy() {
return multiPassStrategy; return multiPassStrategy;
} }
/**
* INTERNAL method to mark cleartext signed messages.
* Do not call this manually.
*/
public void setIsCleartextSigned() {
this.cleartextSigned = true;
}
/**
* Return true if the message is cleartext signed.
* @return cleartext signed
*/
public boolean isCleartextSigned() {
return this.cleartextSigned;
}
} }

View file

@ -16,6 +16,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.openpgp.PGPCompressedData; import org.bouncycastle.openpgp.PGPCompressedData;
@ -126,6 +127,12 @@ public final class DecryptionStreamFactory {
InputStream decoderStream; InputStream decoderStream;
PGPObjectFactory objectFactory; PGPObjectFactory objectFactory;
if (options.isCleartextSigned()) {
inputStream = wrapInVerifySignatureStream(bufferedIn, null);
return new DecryptionStream(inputStream, resultBuilder, integrityProtectedEncryptedInputStream,
null);
}
try { try {
decoderStream = PGPUtilWrapper.getDecoderStream(bufferedIn); decoderStream = PGPUtilWrapper.getDecoderStream(bufferedIn);
decoderStream = CRCingArmoredInputStreamWrapper.possiblyWrap(decoderStream); decoderStream = CRCingArmoredInputStreamWrapper.possiblyWrap(decoderStream);
@ -170,7 +177,7 @@ public final class DecryptionStreamFactory {
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null); (decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
} }
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, PGPObjectFactory objectFactory) { private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, @Nullable PGPObjectFactory objectFactory) {
return new SignatureInputStream.VerifySignatures( return new SignatureInputStream.VerifySignatures(
bufferedIn, objectFactory, onePassSignatureChecks, bufferedIn, objectFactory, onePassSignatureChecks,
onePassSignaturesWithMissingCert, detachedSignatureChecks, options, onePassSignaturesWithMissingCert, detachedSignatureChecks, options,

View file

@ -12,6 +12,7 @@ import java.io.InputStream;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import org.bouncycastle.openpgp.PGPObjectFactory; import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.PGPPublicKeyRing;
@ -46,7 +47,7 @@ public abstract class SignatureInputStream extends FilterInputStream {
public VerifySignatures( public VerifySignatures(
InputStream literalDataStream, InputStream literalDataStream,
PGPObjectFactory objectFactory, @Nullable PGPObjectFactory objectFactory,
List<OnePassSignatureCheck> opSignatures, List<OnePassSignatureCheck> opSignatures,
Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert, Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert,
List<DetachedSignatureCheck> detachedSignatures, List<DetachedSignatureCheck> detachedSignatures,
@ -93,6 +94,9 @@ public abstract class SignatureInputStream extends FilterInputStream {
} }
public void parseAndCombineSignatures() throws IOException { public void parseAndCombineSignatures() throws IOException {
if (objectFactory == null) {
return;
}
// Parse signatures from message // Parse signatures from message
PGPSignatureList signatures; PGPSignatureList signatures;
try { try {

View file

@ -6,7 +6,6 @@ package org.pgpainless.decryption_verification.cleartext_signatures;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.util.logging.Logger;
import org.bouncycastle.bcpg.ArmoredInputStream; import org.bouncycastle.bcpg.ArmoredInputStream;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
@ -27,8 +26,6 @@ import org.pgpainless.util.ArmoredInputStreamFactory;
*/ */
public class CleartextSignatureProcessor { public class CleartextSignatureProcessor {
private static final Logger LOGGER = Logger.getLogger(CleartextSignatureProcessor.class.getName());
private final ArmoredInputStream in; private final ArmoredInputStream in;
private final ConsumerOptions options; private final ConsumerOptions options;
@ -71,6 +68,7 @@ public class CleartextSignatureProcessor {
options.addVerificationOfDetachedSignature(signature); options.addVerificationOfDetachedSignature(signature);
} }
options.setIsCleartextSigned();
return PGPainless.decryptAndOrVerify() return PGPainless.decryptAndOrVerify()
.onInputStream(multiPassStrategy.getMessageInputStream()) .onInputStream(multiPassStrategy.getMessageInputStream())
.withOptions(options); .withOptions(options);

View file

@ -15,6 +15,9 @@ import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.NoSuchAlgorithmException;
import java.util.Random;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKey; import org.bouncycastle.openpgp.PGPPublicKey;
@ -72,6 +75,9 @@ public class CleartextSignatureVerificationTest {
"=Z2SO\n" + "=Z2SO\n" +
"-----END PGP SIGNATURE-----").getBytes(StandardCharsets.UTF_8); "-----END PGP SIGNATURE-----").getBytes(StandardCharsets.UTF_8);
public static final String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
public static final Random random = new Random();
@Test @Test
public void cleartextSignVerification_InMemoryMultiPassStrategy() throws IOException, PGPException { public void cleartextSignVerification_InMemoryMultiPassStrategy() throws IOException, PGPException {
PGPPublicKeyRing signingKeys = TestKeys.getEmilPublicKeyRing(); PGPPublicKeyRing signingKeys = TestKeys.getEmilPublicKeyRing();
@ -228,4 +234,55 @@ public class CleartextSignatureVerificationTest {
OpenPgpMetadata metadata = verificationStream.getResult(); OpenPgpMetadata metadata = verificationStream.getResult();
assertTrue(metadata.isVerified()); assertTrue(metadata.isVerified());
} }
@Test
public void testDecryptionOfVeryLongClearsignedMessage() throws PGPException, InvalidAlgorithmParameterException, NoSuchAlgorithmException, IOException {
String message = randomString(28, 4000);
PGPSecretKeyRing secretKeys = PGPainless.generateKeyRing().modernKeyRing("Alice", null);
ByteArrayOutputStream out = new ByteArrayOutputStream();
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
.onOutputStream(out)
.withOptions(ProducerOptions.sign(
SigningOptions.get()
.addDetachedSignature(SecretKeyRingProtector.unprotectedKeys(),
secretKeys, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT)
).setCleartextSigned());
Streams.pipeAll(new ByteArrayInputStream(message.getBytes(StandardCharsets.UTF_8)), encryptionStream);
encryptionStream.close();
String cleartextSigned = out.toString();
ByteArrayInputStream in = new ByteArrayInputStream(cleartextSigned.getBytes(StandardCharsets.UTF_8));
DecryptionStream decryptionStream = PGPainless.decryptAndOrVerify()
.onInputStream(in)
.withOptions(new ConsumerOptions()
.addVerificationCert(PGPainless.extractCertificate(secretKeys)));
out = new ByteArrayOutputStream();
Streams.pipeAll(decryptionStream, out);
decryptionStream.close();
}
private String randomString(int maxWordLen, int wordCount) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < wordCount; i++) {
sb.append(randomWord(maxWordLen)).append(' ');
int n = random.nextInt(12);
if (n == 11) {
sb.append('\n');
}
}
return sb.toString();
}
private String randomWord(int maxWordLen) {
int len = random.nextInt(maxWordLen);
char[] word = new char[len];
for (int i = 0; i < word.length; i++) {
word[i] = alphabet.charAt(random.nextInt(alphabet.length()));
}
return new String(word);
}
} }