001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.decryption_verification.cleartext_signatures;
006
007import java.io.IOException;
008import java.io.InputStream;
009
010import org.bouncycastle.bcpg.ArmoredInputStream;
011import org.bouncycastle.openpgp.PGPException;
012import org.bouncycastle.openpgp.PGPSignature;
013import org.bouncycastle.openpgp.PGPSignatureList;
014import org.pgpainless.PGPainless;
015import org.pgpainless.algorithm.CompressionAlgorithm;
016import org.pgpainless.algorithm.StreamEncoding;
017import org.pgpainless.decryption_verification.ConsumerOptions;
018import org.pgpainless.decryption_verification.DecryptionStream;
019import org.pgpainless.decryption_verification.OpenPgpMetadata;
020import org.pgpainless.exception.SignatureValidationException;
021import org.pgpainless.util.ArmoredInputStreamFactory;
022
023/**
024 * Processor for cleartext-signed messages.
025 */
026public class CleartextSignatureProcessor {
027
028    private final ArmoredInputStream in;
029    private final ConsumerOptions options;
030
031    public CleartextSignatureProcessor(InputStream inputStream,
032                                       ConsumerOptions options)
033            throws IOException {
034        if (inputStream instanceof ArmoredInputStream) {
035            this.in = (ArmoredInputStream) inputStream;
036        } else {
037            this.in = ArmoredInputStreamFactory.get(inputStream);
038        }
039        this.options = options;
040    }
041
042    /**
043     * Perform the first pass of cleartext signed message processing:
044     * Unpack the message from the ascii armor and detach signatures.
045     * The plaintext message is being written to cache/disk according to the used {@link MultiPassStrategy}.
046     *
047     * The result of this method is a {@link DecryptionStream} which will perform the second pass.
048     * It again outputs the plaintext message and performs signature verification.
049     *
050     * The result of {@link DecryptionStream#getResult()} contains information about the messages signatures.
051     *
052     * @return validated signature
053     * @throws IOException if the signature cannot be read.
054     * @throws PGPException if the signature cannot be initialized.
055     * @throws SignatureValidationException if the signature is invalid.
056     */
057    public DecryptionStream getVerificationStream() throws IOException, PGPException {
058        OpenPgpMetadata.Builder resultBuilder = OpenPgpMetadata.getBuilder();
059        resultBuilder.setCompressionAlgorithm(CompressionAlgorithm.UNCOMPRESSED)
060                .setFileEncoding(StreamEncoding.TEXT);
061
062        MultiPassStrategy multiPassStrategy = options.getMultiPassStrategy();
063        PGPSignatureList signatures = ClearsignedMessageUtil.detachSignaturesFromInbandClearsignedMessage(in, multiPassStrategy.getMessageOutputStream());
064
065        for (PGPSignature signature : signatures) {
066            options.addVerificationOfDetachedSignature(signature);
067        }
068
069        options.setIsCleartextSigned();
070        return PGPainless.decryptAndOrVerify()
071                .onInputStream(multiPassStrategy.getMessageInputStream())
072                .withOptions(options);
073    }
074
075}