001// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
002//
003// SPDX-License-Identifier: Apache-2.0
004
005package org.pgpainless.encryption_signing;
006
007import java.util.Collections;
008import java.util.Date;
009import java.util.HashSet;
010import java.util.Set;
011import javax.annotation.Nonnull;
012
013import org.bouncycastle.openpgp.PGPLiteralData;
014import org.bouncycastle.openpgp.PGPSignature;
015import org.pgpainless.algorithm.CompressionAlgorithm;
016import org.pgpainless.algorithm.StreamEncoding;
017import org.pgpainless.algorithm.SymmetricKeyAlgorithm;
018import org.pgpainless.key.SubkeyIdentifier;
019import org.pgpainless.util.MultiMap;
020
021public final class EncryptionResult {
022
023    private final SymmetricKeyAlgorithm encryptionAlgorithm;
024    private final CompressionAlgorithm compressionAlgorithm;
025
026    private final MultiMap<SubkeyIdentifier, PGPSignature> detachedSignatures;
027    private final Set<SubkeyIdentifier> recipients;
028    private final String fileName;
029    private final Date modificationDate;
030    private final StreamEncoding fileEncoding;
031
032    private EncryptionResult(SymmetricKeyAlgorithm encryptionAlgorithm,
033                             CompressionAlgorithm compressionAlgorithm,
034                             MultiMap<SubkeyIdentifier, PGPSignature> detachedSignatures,
035                             Set<SubkeyIdentifier> recipients,
036                             String fileName,
037                             Date modificationDate,
038                             StreamEncoding encoding) {
039        this.encryptionAlgorithm = encryptionAlgorithm;
040        this.compressionAlgorithm = compressionAlgorithm;
041        this.detachedSignatures = detachedSignatures;
042        this.recipients = Collections.unmodifiableSet(recipients);
043        this.fileName = fileName;
044        this.modificationDate = modificationDate;
045        this.fileEncoding = encoding;
046    }
047
048    /**
049     * Return the symmetric encryption algorithm used to encrypt the message.
050     * @return symmetric encryption algorithm
051     *
052     * @deprecated use {@link #getEncryptionAlgorithm()} instead.
053     */
054    @Deprecated
055    public SymmetricKeyAlgorithm getSymmetricKeyAlgorithm() {
056        return getEncryptionAlgorithm();
057    }
058
059    /**
060     * Return the symmetric encryption algorithm used to encrypt the message.
061     *
062     * @return symmetric encryption algorithm
063     * */
064    public SymmetricKeyAlgorithm getEncryptionAlgorithm() {
065        return encryptionAlgorithm;
066    }
067
068    /**
069     * Return the compression algorithm that was used to compress the message before encryption/signing.
070     *
071     * @return compression algorithm
072     */
073    public CompressionAlgorithm getCompressionAlgorithm() {
074        return compressionAlgorithm;
075    }
076
077    /**
078     * Return a {@link MultiMap} of key identifiers and detached signatures that were generated for the message.
079     * Each key of the map represents a signing key, which has one or more detached signatures associated with it.
080     *
081     * @return detached signatures
082     */
083    public MultiMap<SubkeyIdentifier, PGPSignature> getDetachedSignatures() {
084        return detachedSignatures;
085    }
086
087    /**
088     * Return the set of recipient encryption keys.
089     *
090     * @return recipients
091     */
092    public Set<SubkeyIdentifier> getRecipients() {
093        return recipients;
094    }
095
096    /**
097     * Return the file name of the encrypted/signed data.
098     *
099     * @return filename
100     */
101    public String getFileName() {
102        return fileName;
103    }
104
105    /**
106     * Return the modification date of the encrypted/signed file.
107     *
108     * @return modification date
109     */
110    public Date getModificationDate() {
111        return modificationDate;
112    }
113
114    /**
115     * Return the encoding format of the encrypted/signed data.
116     *
117     * @return encoding format
118     */
119    public StreamEncoding getFileEncoding() {
120        return fileEncoding;
121    }
122
123    /**
124     * Return true, if the message is marked as for-your-eyes-only.
125     * This is typically done by setting the filename "_CONSOLE".
126     *
127     * @return is message for your eyes only?
128     */
129    public boolean isForYourEyesOnly() {
130        return PGPLiteralData.CONSOLE.equals(getFileName());
131    }
132
133    /**
134     * Create a builder for the encryption result class.
135     *
136     * @return builder
137     */
138    public static Builder builder() {
139        return new Builder();
140    }
141
142    public static class Builder {
143
144        private SymmetricKeyAlgorithm encryptionAlgorithm;
145        private CompressionAlgorithm compressionAlgorithm;
146
147        private final MultiMap<SubkeyIdentifier, PGPSignature> detachedSignatures = new MultiMap<>();
148        private final Set<SubkeyIdentifier> recipients = new HashSet<>();
149        private String fileName = "";
150        private Date modificationDate = new Date(0L); // NOW
151        private StreamEncoding encoding = StreamEncoding.BINARY;
152
153        public Builder setEncryptionAlgorithm(SymmetricKeyAlgorithm encryptionAlgorithm) {
154            this.encryptionAlgorithm = encryptionAlgorithm;
155            return this;
156        }
157
158        public Builder setCompressionAlgorithm(CompressionAlgorithm compressionAlgorithm) {
159            this.compressionAlgorithm = compressionAlgorithm;
160            return this;
161        }
162
163        public Builder addRecipient(SubkeyIdentifier recipient) {
164            this.recipients.add(recipient);
165            return this;
166        }
167
168        public Builder addDetachedSignature(SubkeyIdentifier signingSubkeyIdentifier, PGPSignature detachedSignature) {
169            this.detachedSignatures.put(signingSubkeyIdentifier, detachedSignature);
170            return this;
171        }
172
173        public Builder setFileName(@Nonnull String fileName) {
174            this.fileName = fileName;
175            return this;
176        }
177
178        public Builder setModificationDate(@Nonnull Date modificationDate) {
179            this.modificationDate = modificationDate;
180            return this;
181        }
182
183        public Builder setFileEncoding(StreamEncoding fileEncoding) {
184            this.encoding = fileEncoding;
185            return this;
186        }
187
188        public EncryptionResult build() {
189            if (encryptionAlgorithm == null) {
190                throw new IllegalStateException("Encryption algorithm not set.");
191            }
192            if (compressionAlgorithm == null) {
193                throw new IllegalStateException("Compression algorithm not set.");
194            }
195
196            return new EncryptionResult(encryptionAlgorithm, compressionAlgorithm, detachedSignatures, recipients,
197                    fileName, modificationDate, encoding);
198        }
199    }
200}