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.Date; 008import javax.annotation.Nonnull; 009import javax.annotation.Nullable; 010 011import org.bouncycastle.openpgp.PGPLiteralData; 012import org.pgpainless.PGPainless; 013import org.pgpainless.algorithm.CompressionAlgorithm; 014import org.pgpainless.algorithm.StreamEncoding; 015 016public final class ProducerOptions { 017 018 private final EncryptionOptions encryptionOptions; 019 private final SigningOptions signingOptions; 020 private String fileName = ""; 021 private Date modificationDate = PGPLiteralData.NOW; 022 private StreamEncoding streamEncoding = StreamEncoding.BINARY; 023 private boolean cleartextSigned = false; 024 025 private CompressionAlgorithm compressionAlgorithmOverride = PGPainless.getPolicy().getCompressionAlgorithmPolicy() 026 .defaultCompressionAlgorithm(); 027 private boolean asciiArmor = true; 028 029 private ProducerOptions(EncryptionOptions encryptionOptions, SigningOptions signingOptions) { 030 this.encryptionOptions = encryptionOptions; 031 this.signingOptions = signingOptions; 032 } 033 034 /** 035 * Sign and encrypt some data. 036 * 037 * @param encryptionOptions encryption options 038 * @param signingOptions signing options 039 * @return builder 040 */ 041 public static ProducerOptions signAndEncrypt(EncryptionOptions encryptionOptions, 042 SigningOptions signingOptions) { 043 throwIfNull(encryptionOptions); 044 throwIfNull(signingOptions); 045 return new ProducerOptions(encryptionOptions, signingOptions); 046 } 047 048 /** 049 * Sign some data without encryption. 050 * 051 * @param signingOptions signing options 052 * @return builder 053 */ 054 public static ProducerOptions sign(SigningOptions signingOptions) { 055 throwIfNull(signingOptions); 056 return new ProducerOptions(null, signingOptions); 057 } 058 059 /** 060 * Encrypt some data without signing. 061 * 062 * @param encryptionOptions encryption options 063 * @return builder 064 */ 065 public static ProducerOptions encrypt(EncryptionOptions encryptionOptions) { 066 throwIfNull(encryptionOptions); 067 return new ProducerOptions(encryptionOptions, null); 068 } 069 070 public static ProducerOptions noEncryptionNoSigning() { 071 return new ProducerOptions(null, null); 072 } 073 074 private static void throwIfNull(EncryptionOptions encryptionOptions) { 075 if (encryptionOptions == null) { 076 throw new NullPointerException("EncryptionOptions cannot be null."); 077 } 078 } 079 080 private static void throwIfNull(SigningOptions signingOptions) { 081 if (signingOptions == null) { 082 throw new NullPointerException("SigningOptions cannot be null."); 083 } 084 } 085 086 /** 087 * Specify, whether the result of the encryption/signing operation shall be ascii armored. 088 * The default value is true. 089 * 090 * @param asciiArmor ascii armor 091 * @return builder 092 */ 093 public ProducerOptions setAsciiArmor(boolean asciiArmor) { 094 if (cleartextSigned && !asciiArmor) { 095 throw new IllegalArgumentException("Cleartext signing is enabled. Cannot disable ASCII armoring."); 096 } 097 this.asciiArmor = asciiArmor; 098 return this; 099 } 100 101 /** 102 * Return true if the output of the encryption/signing operation shall be ascii armored. 103 * 104 * @return ascii armored 105 */ 106 public boolean isAsciiArmor() { 107 return asciiArmor; 108 } 109 110 public ProducerOptions setCleartextSigned() { 111 if (signingOptions == null) { 112 throw new IllegalArgumentException("Signing Options cannot be null if cleartext signing is enabled."); 113 } 114 if (encryptionOptions != null) { 115 throw new IllegalArgumentException("Cannot encode encrypted message as Cleartext Signed."); 116 } 117 for (SigningOptions.SigningMethod method : signingOptions.getSigningMethods().values()) { 118 if (!method.isDetached()) { 119 throw new IllegalArgumentException("For cleartext signed message, all signatures must be added as detached signatures."); 120 } 121 } 122 cleartextSigned = true; 123 asciiArmor = true; 124 compressionAlgorithmOverride = CompressionAlgorithm.UNCOMPRESSED; 125 return this; 126 } 127 128 public boolean isCleartextSigned() { 129 return cleartextSigned; 130 } 131 132 /** 133 * Set the name of the encrypted file. 134 * Note: This option cannot be used simultaneously with {@link #setForYourEyesOnly()}. 135 * 136 * @param fileName name of the encrypted file 137 * @return this 138 */ 139 public ProducerOptions setFileName(@Nonnull String fileName) { 140 this.fileName = fileName; 141 return this; 142 } 143 144 /** 145 * Return the encrypted files name. 146 * 147 * @return file name 148 */ 149 public String getFileName() { 150 return fileName; 151 } 152 153 /** 154 * Mark the encrypted message as for-your-eyes-only by setting a special file name. 155 * Note: Therefore this method cannot be used simultaneously with {@link #setFileName(String)}. 156 * 157 * @return this 158 */ 159 public ProducerOptions setForYourEyesOnly() { 160 this.fileName = PGPLiteralData.CONSOLE; 161 return this; 162 } 163 164 /** 165 * Set the modification date of the encrypted file. 166 * 167 * @param modificationDate Modification date of the encrypted file. 168 * @return this 169 */ 170 public ProducerOptions setModificationDate(@Nonnull Date modificationDate) { 171 this.modificationDate = modificationDate; 172 return this; 173 } 174 175 /** 176 * Return the modification date of the encrypted file. 177 * 178 * @return modification date 179 */ 180 public Date getModificationDate() { 181 return modificationDate; 182 } 183 184 /** 185 * Set the format of the literal data packet. 186 * Defaults to {@link StreamEncoding#BINARY}. 187 * 188 * @see <a href="https://datatracker.ietf.org/doc/html/rfc4880#section-5.9">RFC4880 ยง5.9. Literal Data Packet</a> 189 * 190 * @param encoding encoding 191 * @return this 192 */ 193 public ProducerOptions setEncoding(@Nonnull StreamEncoding encoding) { 194 this.streamEncoding = encoding; 195 return this; 196 } 197 198 public StreamEncoding getEncoding() { 199 return streamEncoding; 200 } 201 /** 202 * Override which compression algorithm shall be used. 203 * 204 * @param compressionAlgorithm compression algorithm override 205 * @return builder 206 */ 207 public ProducerOptions overrideCompressionAlgorithm(CompressionAlgorithm compressionAlgorithm) { 208 if (compressionAlgorithm == null) { 209 throw new NullPointerException("Compression algorithm cannot be null."); 210 } 211 this.compressionAlgorithmOverride = compressionAlgorithm; 212 return this; 213 } 214 215 public CompressionAlgorithm getCompressionAlgorithmOverride() { 216 return compressionAlgorithmOverride; 217 } 218 219 public @Nullable EncryptionOptions getEncryptionOptions() { 220 return encryptionOptions; 221 } 222 223 public @Nullable SigningOptions getSigningOptions() { 224 return signingOptions; 225 } 226}