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:
parent
50f565dd8c
commit
16e283f3a6
5 changed files with 88 additions and 5 deletions
|
@ -53,6 +53,7 @@ public class ConsumerOptions {
|
|||
private MissingKeyPassphraseStrategy missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE;
|
||||
|
||||
private MultiPassStrategy multiPassStrategy = new InMemoryMultiPassStrategy();
|
||||
private boolean cleartextSigned;
|
||||
|
||||
/**
|
||||
* Consider signatures on the message made before the given timestamp invalid.
|
||||
|
@ -352,4 +353,20 @@ public class ConsumerOptions {
|
|||
public MultiPassStrategy getMultiPassStrategy() {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||
import org.bouncycastle.openpgp.PGPCompressedData;
|
||||
|
@ -126,6 +127,12 @@ public final class DecryptionStreamFactory {
|
|||
InputStream decoderStream;
|
||||
PGPObjectFactory objectFactory;
|
||||
|
||||
if (options.isCleartextSigned()) {
|
||||
inputStream = wrapInVerifySignatureStream(bufferedIn, null);
|
||||
return new DecryptionStream(inputStream, resultBuilder, integrityProtectedEncryptedInputStream,
|
||||
null);
|
||||
}
|
||||
|
||||
try {
|
||||
decoderStream = PGPUtilWrapper.getDecoderStream(bufferedIn);
|
||||
decoderStream = CRCingArmoredInputStreamWrapper.possiblyWrap(decoderStream);
|
||||
|
@ -170,7 +177,7 @@ public final class DecryptionStreamFactory {
|
|||
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
|
||||
}
|
||||
|
||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, PGPObjectFactory objectFactory) {
|
||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, @Nullable PGPObjectFactory objectFactory) {
|
||||
return new SignatureInputStream.VerifySignatures(
|
||||
bufferedIn, objectFactory, onePassSignatureChecks,
|
||||
onePassSignaturesWithMissingCert, detachedSignatureChecks, options,
|
||||
|
|
|
@ -12,6 +12,7 @@ import java.io.InputStream;
|
|||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import org.bouncycastle.openpgp.PGPObjectFactory;
|
||||
import org.bouncycastle.openpgp.PGPPublicKeyRing;
|
||||
|
@ -46,7 +47,7 @@ public abstract class SignatureInputStream extends FilterInputStream {
|
|||
|
||||
public VerifySignatures(
|
||||
InputStream literalDataStream,
|
||||
PGPObjectFactory objectFactory,
|
||||
@Nullable PGPObjectFactory objectFactory,
|
||||
List<OnePassSignatureCheck> opSignatures,
|
||||
Map<Long, OnePassSignatureCheck> onePassSignaturesWithMissingCert,
|
||||
List<DetachedSignatureCheck> detachedSignatures,
|
||||
|
@ -93,6 +94,9 @@ public abstract class SignatureInputStream extends FilterInputStream {
|
|||
}
|
||||
|
||||
public void parseAndCombineSignatures() throws IOException {
|
||||
if (objectFactory == null) {
|
||||
return;
|
||||
}
|
||||
// Parse signatures from message
|
||||
PGPSignatureList signatures;
|
||||
try {
|
||||
|
|
|
@ -6,7 +6,6 @@ package org.pgpainless.decryption_verification.cleartext_signatures;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
||||
import org.bouncycastle.openpgp.PGPException;
|
||||
|
@ -27,8 +26,6 @@ import org.pgpainless.util.ArmoredInputStreamFactory;
|
|||
*/
|
||||
public class CleartextSignatureProcessor {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(CleartextSignatureProcessor.class.getName());
|
||||
|
||||
private final ArmoredInputStream in;
|
||||
private final ConsumerOptions options;
|
||||
|
||||
|
@ -71,6 +68,7 @@ public class CleartextSignatureProcessor {
|
|||
options.addVerificationOfDetachedSignature(signature);
|
||||
}
|
||||
|
||||
options.setIsCleartextSigned();
|
||||
return PGPainless.decryptAndOrVerify()
|
||||
.onInputStream(multiPassStrategy.getMessageInputStream())
|
||||
.withOptions(options);
|
||||
|
|
|
@ -15,6 +15,9 @@ import java.io.File;
|
|||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
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.PGPPublicKey;
|
||||
|
@ -72,6 +75,9 @@ public class CleartextSignatureVerificationTest {
|
|||
"=Z2SO\n" +
|
||||
"-----END PGP SIGNATURE-----").getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
public static final String alphabet = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
|
||||
public static final Random random = new Random();
|
||||
|
||||
@Test
|
||||
public void cleartextSignVerification_InMemoryMultiPassStrategy() throws IOException, PGPException {
|
||||
PGPPublicKeyRing signingKeys = TestKeys.getEmilPublicKeyRing();
|
||||
|
@ -228,4 +234,55 @@ public class CleartextSignatureVerificationTest {
|
|||
OpenPgpMetadata metadata = verificationStream.getResult();
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue