mirror of
https://github.com/pgpainless/pgpainless.git
synced 2024-11-26 22:32:07 +01:00
Simplify handling of cleartext-signed data
This commit is contained in:
parent
db58280db6
commit
1753cef10e
8 changed files with 55 additions and 242 deletions
|
@ -53,7 +53,6 @@ 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.
|
||||||
|
@ -346,21 +345,4 @@ 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 ConsumerOptions setIsCleartextSigned() {
|
|
||||||
this.cleartextSigned = true;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the message is cleartext signed.
|
|
||||||
* @return cleartext signed
|
|
||||||
*/
|
|
||||||
public boolean isCleartextSigned() {
|
|
||||||
return this.cleartextSigned;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,19 +4,14 @@
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification;
|
package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
import java.io.BufferedInputStream;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import javax.annotation.Nonnull;
|
import javax.annotation.Nonnull;
|
||||||
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
import org.bouncycastle.openpgp.PGPException;
|
||||||
import org.pgpainless.decryption_verification.cleartext_signatures.VerifyCleartextSignaturesImpl;
|
|
||||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
|
||||||
|
|
||||||
public class DecryptionBuilder implements DecryptionBuilderInterface {
|
public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
|
|
||||||
public static int BUFFER_SIZE = 4096;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DecryptWith onInputStream(@Nonnull InputStream inputStream) {
|
public DecryptWith onInputStream(@Nonnull InputStream inputStream) {
|
||||||
return new DecryptWithImpl(inputStream);
|
return new DecryptWithImpl(inputStream);
|
||||||
|
@ -24,11 +19,10 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
|
|
||||||
static class DecryptWithImpl implements DecryptWith {
|
static class DecryptWithImpl implements DecryptWith {
|
||||||
|
|
||||||
private final BufferedInputStream inputStream;
|
private final InputStream inputStream;
|
||||||
|
|
||||||
DecryptWithImpl(InputStream inputStream) {
|
DecryptWithImpl(InputStream inputStream) {
|
||||||
this.inputStream = new BufferedInputStream(inputStream, BUFFER_SIZE);
|
this.inputStream = inputStream;
|
||||||
this.inputStream.mark(BUFFER_SIZE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -37,15 +31,7 @@ public class DecryptionBuilder implements DecryptionBuilderInterface {
|
||||||
throw new IllegalArgumentException("Consumer options cannot be null.");
|
throw new IllegalArgumentException("Consumer options cannot be null.");
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
return DecryptionStreamFactory.create(inputStream, consumerOptions);
|
||||||
return DecryptionStreamFactory.create(inputStream, consumerOptions);
|
|
||||||
} catch (WrongConsumingMethodException e) {
|
|
||||||
inputStream.reset();
|
|
||||||
return new VerifyCleartextSignaturesImpl()
|
|
||||||
.onInputStream(inputStream)
|
|
||||||
.withOptions(consumerOptions)
|
|
||||||
.getVerificationStream();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -36,6 +36,7 @@ import org.bouncycastle.openpgp.PGPSecretKey;
|
||||||
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
import org.bouncycastle.openpgp.PGPSecretKeyRing;
|
||||||
import org.bouncycastle.openpgp.PGPSessionKey;
|
import org.bouncycastle.openpgp.PGPSessionKey;
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
import org.bouncycastle.openpgp.PGPSignature;
|
||||||
|
import org.bouncycastle.openpgp.PGPSignatureList;
|
||||||
import org.bouncycastle.openpgp.PGPUtil;
|
import org.bouncycastle.openpgp.PGPUtil;
|
||||||
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory;
|
||||||
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
import org.bouncycastle.openpgp.operator.PGPContentVerifierBuilderProvider;
|
||||||
|
@ -46,6 +47,8 @@ import org.pgpainless.algorithm.CompressionAlgorithm;
|
||||||
import org.pgpainless.algorithm.EncryptionPurpose;
|
import org.pgpainless.algorithm.EncryptionPurpose;
|
||||||
import org.pgpainless.algorithm.StreamEncoding;
|
import org.pgpainless.algorithm.StreamEncoding;
|
||||||
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
|
||||||
|
import org.pgpainless.decryption_verification.cleartext_signatures.ClearsignedMessageUtil;
|
||||||
|
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy;
|
||||||
import org.pgpainless.exception.FinalIOException;
|
import org.pgpainless.exception.FinalIOException;
|
||||||
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
import org.pgpainless.exception.MessageNotIntegrityProtectedException;
|
||||||
import org.pgpainless.exception.MissingDecryptionMethodException;
|
import org.pgpainless.exception.MissingDecryptionMethodException;
|
||||||
|
@ -53,7 +56,6 @@ import org.pgpainless.exception.MissingLiteralDataException;
|
||||||
import org.pgpainless.exception.MissingPassphraseException;
|
import org.pgpainless.exception.MissingPassphraseException;
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
import org.pgpainless.exception.SignatureValidationException;
|
||||||
import org.pgpainless.exception.UnacceptableAlgorithmException;
|
import org.pgpainless.exception.UnacceptableAlgorithmException;
|
||||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
|
||||||
import org.pgpainless.implementation.ImplementationFactory;
|
import org.pgpainless.implementation.ImplementationFactory;
|
||||||
import org.pgpainless.key.SubkeyIdentifier;
|
import org.pgpainless.key.SubkeyIdentifier;
|
||||||
import org.pgpainless.key.info.KeyRingInfo;
|
import org.pgpainless.key.info.KeyRingInfo;
|
||||||
|
@ -62,6 +64,7 @@ import org.pgpainless.key.protection.UnlockSecretKey;
|
||||||
import org.pgpainless.signature.SignatureUtils;
|
import org.pgpainless.signature.SignatureUtils;
|
||||||
import org.pgpainless.signature.consumer.DetachedSignatureCheck;
|
import org.pgpainless.signature.consumer.DetachedSignatureCheck;
|
||||||
import org.pgpainless.signature.consumer.OnePassSignatureCheck;
|
import org.pgpainless.signature.consumer.OnePassSignatureCheck;
|
||||||
|
import org.pgpainless.util.ArmoredInputStreamFactory;
|
||||||
import org.pgpainless.util.CRCingArmoredInputStreamWrapper;
|
import org.pgpainless.util.CRCingArmoredInputStreamWrapper;
|
||||||
import org.pgpainless.util.PGPUtilWrapper;
|
import org.pgpainless.util.PGPUtilWrapper;
|
||||||
import org.pgpainless.util.Passphrase;
|
import org.pgpainless.util.Passphrase;
|
||||||
|
@ -77,6 +80,9 @@ public final class DecryptionStreamFactory {
|
||||||
// Maximum nesting depth of packets (e.g. compression, encryption...)
|
// Maximum nesting depth of packets (e.g. compression, encryption...)
|
||||||
private static final int MAX_PACKET_NESTING_DEPTH = 16;
|
private static final int MAX_PACKET_NESTING_DEPTH = 16;
|
||||||
|
|
||||||
|
// Buffer Size for BufferedInputStreams
|
||||||
|
public static int BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
private final ConsumerOptions options;
|
private final ConsumerOptions options;
|
||||||
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
private final OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
||||||
private final List<OnePassSignatureCheck> onePassSignatureChecks = new ArrayList<>();
|
private final List<OnePassSignatureCheck> onePassSignatureChecks = new ArrayList<>();
|
||||||
|
@ -92,7 +98,8 @@ public final class DecryptionStreamFactory {
|
||||||
@Nonnull ConsumerOptions options)
|
@Nonnull ConsumerOptions options)
|
||||||
throws PGPException, IOException {
|
throws PGPException, IOException {
|
||||||
DecryptionStreamFactory factory = new DecryptionStreamFactory(options);
|
DecryptionStreamFactory factory = new DecryptionStreamFactory(options);
|
||||||
return factory.parseOpenPGPDataAndCreateDecryptionStream(inputStream);
|
BufferedInputStream bufferedIn = new BufferedInputStream(inputStream, BUFFER_SIZE);
|
||||||
|
return factory.parseOpenPGPDataAndCreateDecryptionStream(bufferedIn);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DecryptionStreamFactory(ConsumerOptions options) {
|
public DecryptionStreamFactory(ConsumerOptions options) {
|
||||||
|
@ -125,44 +132,34 @@ public final class DecryptionStreamFactory {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private DecryptionStream parseOpenPGPDataAndCreateDecryptionStream(InputStream inputStream)
|
private DecryptionStream parseOpenPGPDataAndCreateDecryptionStream(BufferedInputStream bufferedIn)
|
||||||
throws IOException, PGPException {
|
throws IOException, PGPException {
|
||||||
// Make sure we handle armored and non-armored data properly
|
InputStream pgpInStream;
|
||||||
BufferedInputStream bufferedIn = new BufferedInputStream(inputStream, 512);
|
InputStream outerDecodingStream;
|
||||||
bufferedIn.mark(512);
|
|
||||||
InputStream decoderStream;
|
|
||||||
PGPObjectFactory objectFactory;
|
PGPObjectFactory objectFactory;
|
||||||
|
|
||||||
// Workaround for cleartext signed data
|
|
||||||
// If we below threw a WrongConsumingMethodException, the CleartextSignatureProcessor will prepare the
|
|
||||||
// message for us and will set options.isCleartextSigned() to true.
|
|
||||||
// That way we can process long messages without running the issue of resetting the bufferedInputStream
|
|
||||||
// to invalid marks.
|
|
||||||
if (options.isCleartextSigned()) {
|
|
||||||
inputStream = wrapInVerifySignatureStream(bufferedIn, null);
|
|
||||||
return new DecryptionStream(inputStream, resultBuilder, integrityProtectedEncryptedInputStream,
|
|
||||||
null);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
decoderStream = PGPUtilWrapper.getDecoderStream(bufferedIn);
|
outerDecodingStream = PGPUtilWrapper.getDecoderStream(bufferedIn);
|
||||||
decoderStream = CRCingArmoredInputStreamWrapper.possiblyWrap(decoderStream);
|
outerDecodingStream = CRCingArmoredInputStreamWrapper.possiblyWrap(outerDecodingStream);
|
||||||
|
|
||||||
if (decoderStream instanceof ArmoredInputStream) {
|
if (outerDecodingStream instanceof ArmoredInputStream) {
|
||||||
ArmoredInputStream armor = (ArmoredInputStream) decoderStream;
|
ArmoredInputStream armor = (ArmoredInputStream) outerDecodingStream;
|
||||||
|
|
||||||
// Cleartext Signed Message
|
// Cleartext Signed Message
|
||||||
// Throw a WrongConsumingMethodException to delegate preparation (extraction of signatures)
|
// Throw a WrongConsumingMethodException to delegate preparation (extraction of signatures)
|
||||||
// to the CleartextSignatureProcessor which will call us again (see comment above)
|
// to the CleartextSignatureProcessor which will call us again (see comment above)
|
||||||
if (armor.isClearText()) {
|
if (armor.isClearText()) {
|
||||||
throw new WrongConsumingMethodException("Message appears to be using the Cleartext Signature Framework. " +
|
bufferedIn.reset();
|
||||||
"Use PGPainless.verifyCleartextSignedMessage() to verify this message instead.");
|
return parseCleartextSignedMessage(bufferedIn);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(decoderStream);
|
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream);
|
||||||
// Parse OpenPGP message
|
// Parse OpenPGP message
|
||||||
inputStream = processPGPPackets(objectFactory, 1);
|
pgpInStream = processPGPPackets(objectFactory, 1);
|
||||||
|
return new DecryptionStream(pgpInStream,
|
||||||
|
resultBuilder, integrityProtectedEncryptedInputStream,
|
||||||
|
(outerDecodingStream instanceof ArmoredInputStream) ? outerDecodingStream : null);
|
||||||
} catch (EOFException | FinalIOException e) {
|
} catch (EOFException | FinalIOException e) {
|
||||||
// Broken message or invalid decryption session key
|
// Broken message or invalid decryption session key
|
||||||
throw e;
|
throw e;
|
||||||
|
@ -172,23 +169,44 @@ public final class DecryptionStreamFactory {
|
||||||
// to allow for detached signature verification.
|
// to allow for detached signature verification.
|
||||||
LOGGER.debug("The message appears to not be an OpenPGP message. This is probably data signed with detached signatures?");
|
LOGGER.debug("The message appears to not be an OpenPGP message. This is probably data signed with detached signatures?");
|
||||||
bufferedIn.reset();
|
bufferedIn.reset();
|
||||||
decoderStream = bufferedIn;
|
outerDecodingStream = bufferedIn;
|
||||||
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(decoderStream);
|
objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(outerDecodingStream);
|
||||||
inputStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
|
pgpInStream = wrapInVerifySignatureStream(bufferedIn, objectFactory);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
if (e.getMessage().contains("invalid armor") || e.getMessage().contains("invalid header encountered")) {
|
if (e.getMessage().contains("invalid armor") || e.getMessage().contains("invalid header encountered")) {
|
||||||
// We falsely assumed the data to be armored.
|
// We falsely assumed the data to be armored.
|
||||||
LOGGER.debug("The message is apparently not armored.");
|
LOGGER.debug("The message is apparently not armored.");
|
||||||
bufferedIn.reset();
|
bufferedIn.reset();
|
||||||
decoderStream = bufferedIn;
|
outerDecodingStream = CRCingArmoredInputStreamWrapper.possiblyWrap(bufferedIn);
|
||||||
inputStream = wrapInVerifySignatureStream(bufferedIn, null);
|
pgpInStream = wrapInVerifySignatureStream(outerDecodingStream, null);
|
||||||
} else {
|
} else {
|
||||||
throw new FinalIOException(e);
|
throw new FinalIOException(e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DecryptionStream(inputStream, resultBuilder, integrityProtectedEncryptedInputStream,
|
return new DecryptionStream(pgpInStream, resultBuilder, integrityProtectedEncryptedInputStream,
|
||||||
(decoderStream instanceof ArmoredInputStream) ? decoderStream : null);
|
(outerDecodingStream instanceof ArmoredInputStream) ? outerDecodingStream : null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private DecryptionStream parseCleartextSignedMessage(BufferedInputStream in)
|
||||||
|
throws IOException, PGPException {
|
||||||
|
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
|
||||||
|
.setFileEncoding(StreamEncoding.TEXT);
|
||||||
|
|
||||||
|
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(in);
|
||||||
|
|
||||||
|
MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
|
||||||
|
PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(armorIn, multiPassStrategy.getMessageOutputStream());
|
||||||
|
|
||||||
|
for (PGPSignature signature : signatures) {
|
||||||
|
options.addVerificationOfDetachedSignature(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
initializeDetachedSignatures(options.getDetachedSignatures());
|
||||||
|
|
||||||
|
InputStream verifyIn = wrapInVerifySignatureStream(multiPassStrategy.getMessageInputStream(), null);
|
||||||
|
return new DecryptionStream(verifyIn, resultBuilder, integrityProtectedEncryptedInputStream,
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, @Nullable PGPObjectFactory objectFactory) {
|
private InputStream wrapInVerifySignatureStream(InputStream bufferedIn, @Nullable PGPObjectFactory objectFactory) {
|
||||||
|
|
|
@ -1,75 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification.cleartext_signatures;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.bouncycastle.bcpg.ArmoredInputStream;
|
|
||||||
import org.bouncycastle.openpgp.PGPException;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignature;
|
|
||||||
import org.bouncycastle.openpgp.PGPSignatureList;
|
|
||||||
import org.pgpainless.PGPainless;
|
|
||||||
import org.pgpainless.algorithm.CompressionAlgorithm;
|
|
||||||
import org.pgpainless.algorithm.StreamEncoding;
|
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions;
|
|
||||||
import org.pgpainless.decryption_verification.DecryptionStream;
|
|
||||||
import org.pgpainless.decryption_verification.OpenPgpMetadata;
|
|
||||||
import org.pgpainless.exception.SignatureValidationException;
|
|
||||||
import org.pgpainless.util.ArmoredInputStreamFactory;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Processor for cleartext-signed messages.
|
|
||||||
*/
|
|
||||||
public class CleartextSignatureProcessor {
|
|
||||||
|
|
||||||
private final ArmoredInputStream in;
|
|
||||||
private final ConsumerOptions options;
|
|
||||||
|
|
||||||
public CleartextSignatureProcessor(InputStream inputStream,
|
|
||||||
ConsumerOptions options)
|
|
||||||
throws IOException {
|
|
||||||
if (inputStream instanceof ArmoredInputStream) {
|
|
||||||
this.in = (ArmoredInputStream) inputStream;
|
|
||||||
} else {
|
|
||||||
this.in = ArmoredInputStreamFactory.get(inputStream);
|
|
||||||
}
|
|
||||||
this.options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform the first pass of cleartext signed message processing:
|
|
||||||
* Unpack the message from the ascii armor and detach signatures.
|
|
||||||
* The plaintext message is being written to cache/disk according to the used {@link MultiPassStrategy}.
|
|
||||||
*
|
|
||||||
* The result of this method is a {@link DecryptionStream} which will perform the second pass.
|
|
||||||
* It again outputs the plaintext message and performs signature verification.
|
|
||||||
*
|
|
||||||
* The result of {@link DecryptionStream#getResult()} contains information about the messages signatures.
|
|
||||||
*
|
|
||||||
* @return validated signature
|
|
||||||
* @throws IOException if the signature cannot be read.
|
|
||||||
* @throws PGPException if the signature cannot be initialized.
|
|
||||||
* @throws SignatureValidationException if the signature is invalid.
|
|
||||||
*/
|
|
||||||
public DecryptionStream getVerificationStream() throws IOException, PGPException {
|
|
||||||
OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
|
|
||||||
resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
|
|
||||||
.setFileEncoding(StreamEncoding.TEXT);
|
|
||||||
|
|
||||||
MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
|
|
||||||
PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(in, multiPassStrategy.getMessageOutputStream());
|
|
||||||
|
|
||||||
for (PGPSignature signature : signatures) {
|
|
||||||
options.addVerificationOfDetachedSignature(signature);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.setIsCleartextSigned();
|
|
||||||
return PGPainless.decryptAndOrVerify()
|
|
||||||
.onInputStream(multiPassStrategy.getMessageInputStream())
|
|
||||||
.withOptions(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -11,7 +11,7 @@ import java.io.InputStream;
|
||||||
import java.io.OutputStream;
|
import java.io.OutputStream;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Since the {@link CleartextSignatureProcessor} needs to read the whole data twice in order to verify signatures,
|
* Since for verification of cleartext signed messages, we need to read the whole data twice in order to verify signatures,
|
||||||
* a strategy for how to cache the read data is required.
|
* a strategy for how to cache the read data is required.
|
||||||
* Otherwise, large data kept in memory could cause {@link OutOfMemoryError OutOfMemoryErrors} or other issues.
|
* Otherwise, large data kept in memory could cause {@link OutOfMemoryError OutOfMemoryErrors} or other issues.
|
||||||
*
|
*
|
||||||
|
|
|
@ -1,36 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification.cleartext_signatures;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Interface defining the API for verification of cleartext signed documents.
|
|
||||||
*/
|
|
||||||
public interface VerifyCleartextSignatures {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Provide the {@link InputStream} which contains the cleartext-signed message.
|
|
||||||
* @param inputStream inputstream
|
|
||||||
* @return api handle
|
|
||||||
*/
|
|
||||||
VerifyWith onInputStream(InputStream inputStream);
|
|
||||||
|
|
||||||
interface VerifyWith {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Pass in consumer options like verification certificates, acceptable date ranges etc.
|
|
||||||
*
|
|
||||||
* @param options options
|
|
||||||
* @return processor
|
|
||||||
* @throws IOException in case of an IO error
|
|
||||||
*/
|
|
||||||
CleartextSignatureProcessor withOptions(ConsumerOptions options) throws IOException;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,30 +0,0 @@
|
||||||
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
|
|
||||||
//
|
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||||
|
|
||||||
package org.pgpainless.decryption_verification.cleartext_signatures;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
|
|
||||||
import org.pgpainless.decryption_verification.ConsumerOptions;
|
|
||||||
|
|
||||||
public class VerifyCleartextSignaturesImpl implements VerifyCleartextSignatures {
|
|
||||||
|
|
||||||
private InputStream inputStream;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public VerifyWithImpl onInputStream(InputStream inputStream) {
|
|
||||||
VerifyCleartextSignaturesImpl.this.inputStream = inputStream;
|
|
||||||
return new VerifyWithImpl();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class VerifyWithImpl implements VerifyWith {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public CleartextSignatureProcessor withOptions(ConsumerOptions options) throws IOException {
|
|
||||||
return new CleartextSignatureProcessor(inputStream, options);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -6,7 +6,6 @@ package org.pgpainless.decryption_verification;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
|
@ -30,11 +29,9 @@ import org.pgpainless.PGPainless;
|
||||||
import org.pgpainless.algorithm.DocumentSignatureType;
|
import org.pgpainless.algorithm.DocumentSignatureType;
|
||||||
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy;
|
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy;
|
||||||
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy;
|
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.EncryptionStream;
|
||||||
import org.pgpainless.encryption_signing.ProducerOptions;
|
import org.pgpainless.encryption_signing.ProducerOptions;
|
||||||
import org.pgpainless.encryption_signing.SigningOptions;
|
import org.pgpainless.encryption_signing.SigningOptions;
|
||||||
import org.pgpainless.exception.WrongConsumingMethodException;
|
|
||||||
import org.pgpainless.key.TestKeys;
|
import org.pgpainless.key.TestKeys;
|
||||||
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
import org.pgpainless.key.protection.SecretKeyRingProtector;
|
||||||
import org.pgpainless.signature.consumer.CertificateValidator;
|
import org.pgpainless.signature.consumer.CertificateValidator;
|
||||||
|
@ -180,35 +177,6 @@ public class CleartextSignatureVerificationTest {
|
||||||
assertEquals(1, metadata.getVerifiedSignatures().size());
|
assertEquals(1, metadata.getVerifiedSignatures().size());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void consumingInlineSignedMessageWithCleartextSignedVerificationApiThrowsWrongConsumingMethodException()
|
|
||||||
throws 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, () ->
|
|
||||||
new VerifyCleartextSignaturesImpl()
|
|
||||||
.onInputStream(new ByteArrayInputStream(inlineSignedMessage.getBytes(StandardCharsets.UTF_8)))
|
|
||||||
.withOptions(options)
|
|
||||||
.getVerificationStream());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void getDecoderStreamMistakensPlaintextForBase64RegressionTest()
|
public void getDecoderStreamMistakensPlaintextForBase64RegressionTest()
|
||||||
throws PGPException, IOException {
|
throws PGPException, IOException {
|
||||||
|
|
Loading…
Reference in a new issue