001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.sop;
006
007import java.io.IOException;
008import java.io.InputStream;
009import java.util.ArrayList;
010import java.util.Date;
011import java.util.List;
012
013import org.bouncycastle.openpgp.PGPException;
014import org.bouncycastle.openpgp.PGPPublicKeyRingCollection;
015import org.bouncycastle.openpgp.PGPSignature;
016import org.bouncycastle.util.io.Streams;
017import org.pgpainless.PGPainless;
018import org.pgpainless.decryption_verification.ConsumerOptions;
019import org.pgpainless.decryption_verification.DecryptionStream;
020import org.pgpainless.decryption_verification.OpenPgpMetadata;
021import org.pgpainless.key.SubkeyIdentifier;
022import sop.Verification;
023import sop.exception.SOPGPException;
024import sop.operation.Verify;
025
026public class VerifyImpl implements Verify {
027
028    private final ConsumerOptions options = new ConsumerOptions();
029
030    @Override
031    public Verify notBefore(Date timestamp) throws SOPGPException.UnsupportedOption {
032        options.verifyNotBefore(timestamp);
033        return this;
034    }
035
036    @Override
037    public Verify notAfter(Date timestamp) throws SOPGPException.UnsupportedOption {
038        options.verifyNotAfter(timestamp);
039        return this;
040    }
041
042    @Override
043    public Verify cert(InputStream cert) throws SOPGPException.BadData {
044        PGPPublicKeyRingCollection certificates;
045        try {
046            certificates = PGPainless.readKeyRing().publicKeyRingCollection(cert);
047        } catch (IOException | PGPException e) {
048            throw new SOPGPException.BadData(e);
049        }
050        options.addVerificationCerts(certificates);
051        return this;
052    }
053
054    @Override
055    public VerifyImpl signatures(InputStream signatures) throws SOPGPException.BadData {
056        try {
057            options.addVerificationOfDetachedSignatures(signatures);
058        } catch (IOException | PGPException e) {
059            throw new SOPGPException.BadData(e);
060        }
061        return this;
062    }
063
064    @Override
065    public List<Verification> data(InputStream data) throws IOException, SOPGPException.NoSignature, SOPGPException.BadData {
066        DecryptionStream decryptionStream;
067        try {
068            decryptionStream = PGPainless.decryptAndOrVerify()
069                    .onInputStream(data)
070                    .withOptions(options);
071
072            Streams.drain(decryptionStream);
073            decryptionStream.close();
074
075            OpenPgpMetadata metadata = decryptionStream.getResult();
076            List<Verification> verificationList = new ArrayList<>();
077
078            for (SubkeyIdentifier verifiedSigningKey : metadata.getVerifiedSignatures().keySet()) {
079                PGPSignature signature = metadata.getVerifiedSignatures().get(verifiedSigningKey);
080                verificationList.add(new Verification(
081                        signature.getCreationTime(),
082                        verifiedSigningKey.getSubkeyFingerprint().toString(),
083                        verifiedSigningKey.getPrimaryKeyFingerprint().toString()));
084            }
085
086            if (!options.getCertificates().isEmpty()) {
087                if (verificationList.isEmpty()) {
088                    throw new SOPGPException.NoSignature();
089                }
090            }
091
092            return verificationList;
093        } catch (PGPException e) {
094            throw new SOPGPException.BadData(e);
095        }
096    }
097}