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}