diff --git a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/ProducerOptions.java b/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/ProducerOptions.java deleted file mode 100644 index 00fbb10a..00000000 --- a/pgpainless-core/src/main/java/org/pgpainless/encryption_signing/ProducerOptions.java +++ /dev/null @@ -1,355 +0,0 @@ -// SPDX-FileCopyrightText: 2021 Paul Schaub -// -// SPDX-License-Identifier: Apache-2.0 - -package org.pgpainless.encryption_signing; - -import java.util.Date; -import javax.annotation.Nonnull; -import javax.annotation.Nullable; - -import org.bouncycastle.openpgp.PGPLiteralData; -import org.pgpainless.PGPainless; -import org.pgpainless.algorithm.CompressionAlgorithm; -import org.pgpainless.algorithm.StreamEncoding; - -public final class ProducerOptions { - - private final EncryptionOptions encryptionOptions; - private final SigningOptions signingOptions; - private String fileName = ""; - private Date modificationDate = PGPLiteralData.NOW; - private StreamEncoding encodingField = StreamEncoding.BINARY; - private boolean applyCRLFEncoding = false; - private boolean cleartextSigned = false; - private boolean hideArmorHeaders = false; - - private CompressionAlgorithm compressionAlgorithmOverride = PGPainless.getPolicy().getCompressionAlgorithmPolicy() - .defaultCompressionAlgorithm(); - private boolean asciiArmor = true; - private String comment = null; - private String version = null; - - private ProducerOptions(EncryptionOptions encryptionOptions, SigningOptions signingOptions) { - this.encryptionOptions = encryptionOptions; - this.signingOptions = signingOptions; - } - - /** - * Sign and encrypt some data. - * - * @param encryptionOptions encryption options - * @param signingOptions signing options - * @return builder - */ - public static ProducerOptions signAndEncrypt(EncryptionOptions encryptionOptions, - SigningOptions signingOptions) { - throwIfNull(encryptionOptions); - throwIfNull(signingOptions); - return new ProducerOptions(encryptionOptions, signingOptions); - } - - /** - * Sign some data without encryption. - * - * @param signingOptions signing options - * @return builder - */ - public static ProducerOptions sign(SigningOptions signingOptions) { - throwIfNull(signingOptions); - return new ProducerOptions(null, signingOptions); - } - - /** - * Encrypt some data without signing. - * - * @param encryptionOptions encryption options - * @return builder - */ - public static ProducerOptions encrypt(EncryptionOptions encryptionOptions) { - throwIfNull(encryptionOptions); - return new ProducerOptions(encryptionOptions, null); - } - - /** - * Only wrap the data in an OpenPGP packet. - * No encryption or signing will be applied. - * - * @return builder - */ - public static ProducerOptions noEncryptionNoSigning() { - return new ProducerOptions(null, null); - } - - private static void throwIfNull(EncryptionOptions encryptionOptions) { - if (encryptionOptions == null) { - throw new NullPointerException("EncryptionOptions cannot be null."); - } - } - - private static void throwIfNull(SigningOptions signingOptions) { - if (signingOptions == null) { - throw new NullPointerException("SigningOptions cannot be null."); - } - } - - /** - * Specify, whether the result of the encryption/signing operation shall be ascii armored. - * The default value is true. - * - * @param asciiArmor ascii armor - * @return builder - */ - public ProducerOptions setAsciiArmor(boolean asciiArmor) { - if (cleartextSigned && !asciiArmor) { - throw new IllegalArgumentException("Cleartext signing is enabled. Cannot disable ASCII armoring."); - } - this.asciiArmor = asciiArmor; - return this; - } - - /** - * Return true if the output of the encryption/signing operation shall be ascii armored. - * - * @return ascii armored - */ - public boolean isAsciiArmor() { - return asciiArmor; - } - - /** - * Set the comment header in ASCII armored output. - * The default value is null, which means no comment header is added. - * Multiline comments are possible using '\\n'. - *
- * Note: If a default header comment is set using {@link org.pgpainless.util.ArmoredOutputStreamFactory#setComment(String)}, - * then both comments will be written to the produced ASCII armor. - * - * @param comment comment header text - * @return builder - */ - public ProducerOptions setComment(String comment) { - this.comment = comment; - return this; - } - - /** - * Set the version header in ASCII armored output. - * The default value is null, which means no version header is added. - *
- * Note: If the value is non-null, then this method overrides the default version header set using - * {@link org.pgpainless.util.ArmoredOutputStreamFactory#setVersionInfo(String)}. - * - * @param version version header, or null for no version info. - * @return builder - */ - public ProducerOptions setVersion(String version) { - this.version = version; - return this; - } - - /** - * Return comment set for header in ascii armored output. - * - * @return comment - */ - public String getComment() { - return comment; - } - - /** - * Return the version info header in ascii armored output. - * - * @return version info - */ - public String getVersion() { - return version; - } - - /** - * Return whether a comment was set (!= null). - * - * @return true if commend is set - */ - public boolean hasComment() { - return comment != null; - } - - /** - * Return whether a version header was set (!= null). - * - * @return true if version header is set - */ - public boolean hasVersion() { - return version != null; - } - - public ProducerOptions setCleartextSigned() { - if (signingOptions == null) { - throw new IllegalArgumentException("Signing Options cannot be null if cleartext signing is enabled."); - } - if (encryptionOptions != null) { - throw new IllegalArgumentException("Cannot encode encrypted message as Cleartext Signed."); - } - for (SigningOptions.SigningMethod method : signingOptions.getSigningMethods().values()) { - if (!method.isDetached()) { - throw new IllegalArgumentException("For cleartext signed message, all signatures must be added as detached signatures."); - } - } - cleartextSigned = true; - asciiArmor = true; - compressionAlgorithmOverride = CompressionAlgorithm.UNCOMPRESSED; - return this; - } - - public boolean isCleartextSigned() { - return cleartextSigned; - } - - /** - * Set the name of the encrypted file. - * Note: This option cannot be used simultaneously with {@link #setForYourEyesOnly()}. - * - * @param fileName name of the encrypted file - * @return this - */ - public ProducerOptions setFileName(@Nonnull String fileName) { - this.fileName = fileName; - return this; - } - - /** - * Return the encrypted files name. - * - * @return file name - */ - public String getFileName() { - return fileName; - } - - /** - * Mark the encrypted message as for-your-eyes-only by setting a special file name. - * Note: Therefore this method cannot be used simultaneously with {@link #setFileName(String)}. - * - * @return this - * @deprecated deprecated since at least crypto-refresh-05. It is not recommended using this special filename in - * newly generated literal data packets - */ - @Deprecated - public ProducerOptions setForYourEyesOnly() { - this.fileName = PGPLiteralData.CONSOLE; - return this; - } - - /** - * Set the modification date of the encrypted file. - * - * @param modificationDate Modification date of the encrypted file. - * @return this - */ - public ProducerOptions setModificationDate(@Nonnull Date modificationDate) { - this.modificationDate = modificationDate; - return this; - } - - /** - * Return the modification date of the encrypted file. - * - * @return modification date - */ - public Date getModificationDate() { - return modificationDate; - } - - /** - * Set format metadata field of the literal data packet. - * Defaults to {@link StreamEncoding#BINARY}. - *
- * This does not change the encoding of the wrapped data itself. - * To apply CR/LF encoding to your input data before processing, use {@link #applyCRLFEncoding()} instead. - * - * @see RFC4880 §5.9. Literal Data Packet - * - * @param encoding encoding - * @return this - * - * @deprecated options other than the default value of {@link StreamEncoding#BINARY} are discouraged. - */ - @Deprecated - public ProducerOptions setEncoding(@Nonnull StreamEncoding encoding) { - this.encodingField = encoding; - return this; - } - - public StreamEncoding getEncoding() { - return encodingField; - } - - /** - * Apply special encoding of line endings to the input data. - * By default, this is disabled, which means that the data is not altered. - *
- * Enabling it will change the line endings to CR/LF. - * Note: The encoding will not be reversed when decrypting, so applying CR/LF encoding will result in - * the identity "decrypt(encrypt(data)) == data == verify(sign(data))". - * - * @return this - */ - public ProducerOptions applyCRLFEncoding() { - this.applyCRLFEncoding = true; - return this; - } - - /** - * Return the input encoding that will be applied before signing / encryption. - * - * @return input encoding - */ - public boolean isApplyCRLFEncoding() { - return applyCRLFEncoding; - } - - /** - * Override which compression algorithm shall be used. - * - * @param compressionAlgorithm compression algorithm override - * @return builder - */ - public ProducerOptions overrideCompressionAlgorithm(CompressionAlgorithm compressionAlgorithm) { - if (compressionAlgorithm == null) { - throw new NullPointerException("Compression algorithm cannot be null."); - } - this.compressionAlgorithmOverride = compressionAlgorithm; - return this; - } - - public CompressionAlgorithm getCompressionAlgorithmOverride() { - return compressionAlgorithmOverride; - } - - public @Nullable EncryptionOptions getEncryptionOptions() { - return encryptionOptions; - } - - public @Nullable SigningOptions getSigningOptions() { - return signingOptions; - } - - public boolean isHideArmorHeaders() { - return hideArmorHeaders; - } - - /** - * If set to
true
, armor headers like version or comments will be omitted from armored output. - * By default, armor headers are not hidden. - * Note: If comments are added via {@link #setComment(String)}, those are not omitted, even if - * {@link #hideArmorHeaders} is set to
true
. - * - * @param hideArmorHeaders true or false - * @return this - */ - public ProducerOptions setHideArmorHeaders(boolean hideArmorHeaders) { - this.hideArmorHeaders = hideArmorHeaders; - return this; - } -} diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt new file mode 100644 index 00000000..bdc153e9 --- /dev/null +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/ProducerOptions.kt @@ -0,0 +1,291 @@ +// SPDX-FileCopyrightText: 2023 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.encryption_signing + +import org.bouncycastle.openpgp.PGPLiteralData +import org.pgpainless.PGPainless +import org.pgpainless.algorithm.CompressionAlgorithm +import org.pgpainless.algorithm.StreamEncoding +import java.util.* + +class ProducerOptions private constructor( + val encryptionOptions: EncryptionOptions?, + val signingOptions: SigningOptions?) { + + private var _fileName: String = "" + private var _modificationDate: Date = PGPLiteralData.NOW + private var encodingField: StreamEncoding = StreamEncoding.BINARY + private var applyCRLFEncoding = false + private var cleartextSigned = false + private var _hideArmorHeaders = false + + private var _compressionAlgorithmOverride: CompressionAlgorithm = PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm + private var asciiArmor = true + private var _comment: String? = null + private var _version: String? = null + + /** + * Specify, whether the result of the encryption/signing operation shall be ascii armored. + * The default value is true. + * + * @param asciiArmor ascii armor + * @return builder + */ + fun setAsciiArmor(asciiArmor: Boolean) = apply { + require(!(cleartextSigned && !asciiArmor)) { + "Cleartext signing is enabled. Cannot disable ASCII armoring." + } + this.asciiArmor = asciiArmor + } + + /** + * Return true if the output of the encryption/signing operation shall be ascii armored. + * + * @return ascii armored + */ + val isAsciiArmor: Boolean + get() = asciiArmor + + /** + * Set the comment header in ASCII armored output. + * The default value is null, which means no comment header is added. + * Multiline comments are possible using '\\n'. + *
+ * Note: If a default header comment is set using [org.pgpainless.util.ArmoredOutputStreamFactory.setComment], + * then both comments will be written to the produced ASCII armor. + * + * @param comment comment header text + * @return builder + */ + fun setComment(comment: String?) = apply { + _comment = comment + } + + /** + * Return comment set for header in ascii armored output. + * + * @return comment + */ + val comment: String? + get() = _comment + + /** + * Return whether a comment was set (!= null). + * + * @return true if commend is set + */ + fun hasComment() = _comment != null + + /** + * Set the version header in ASCII armored output. + * The default value is null, which means no version header is added. + *
+ * Note: If the value is non-null, then this method overrides the default version header set using + * [org.pgpainless.util.ArmoredOutputStreamFactory.setVersionInfo]. + * + * @param version version header, or null for no version info. + * @return builder + */ + fun setVersion(version: String?) = apply { + _version = version + } + + /** + * Return the version info header in ascii armored output. + * + * @return version info + */ + val version: String? + get() = _version + + /** + * Return whether a version header was set (!= null). + * + * @return true if version header is set + */ + fun hasVersion() = version != null + + fun setCleartextSigned() = apply { + require(signingOptions != null) { + "Signing Options cannot be null if cleartext signing is enabled." + } + require(encryptionOptions == null) { + "Cannot encode encrypted message as Cleartext Signed." + } + require(signingOptions.signingMethods.values.all { it.isDetached }) { + "For cleartext signed messages, all signatures must be added as detached signatures." + } + + cleartextSigned = true + asciiArmor = true + _compressionAlgorithmOverride = CompressionAlgorithm.UNCOMPRESSED + } + + val isCleartextSigned: Boolean + get() = cleartextSigned + + /** + * Set the name of the encrypted file. + * Note: This option cannot be used simultaneously with [setForYourEyesOnly]. + * + * @param fileName name of the encrypted file + * @return this + */ + fun setFileName(fileName: String) = apply { + _fileName = fileName + } + + /** + * Return the encrypted files name. + * + * @return file name + */ + val fileName: String + get() = _fileName + + /** + * Mark the encrypted message as for-your-eyes-only by setting a special file name. + * Note: Therefore this method cannot be used simultaneously with [setFileName]. + * + * @return this + * @deprecated deprecated since at least crypto-refresh-05. It is not recommended using this special filename in + * newly generated literal data packets + */ + @Deprecated("Signaling using special file name is discouraged.") + fun setForYourEyesOnly() = apply { + _fileName = PGPLiteralData.CONSOLE + } + + /** + * Set the modification date of the encrypted file. + * + * @param modificationDate Modification date of the encrypted file. + * @return this + */ + fun setModificationDate(modificationDate: Date) = apply { + _modificationDate = modificationDate + } + + /** + * Return the modification date of the encrypted file. + * + * @return modification date + */ + val modificationDate: Date + get() = _modificationDate + + /** + * Set format metadata field of the literal data packet. + * Defaults to [StreamEncoding.BINARY]. + *
+ * This does not change the encoding of the wrapped data itself. + * To apply CR/LF encoding to your input data before processing, use [applyCRLFEncoding] instead. + * + * @see RFC4880 §5.9. Literal Data Packet + * + * @param encoding encoding + * @return this + * + * @deprecated options other than the default value of {@link StreamEncoding#BINARY} are discouraged. + */ + @Deprecated("Options other than BINARY are discouraged.") + fun setEncoding(encoding: StreamEncoding) = apply { + encodingField = encoding + } + + val encoding: StreamEncoding + get() = encodingField + + /** + * Apply special encoding of line endings to the input data. + * By default, this is disabled, which means that the data is not altered. + *
+ * Enabling it will change the line endings to CR/LF. + * Note: The encoding will not be reversed when decrypting, so applying CR/LF encoding will result in + * the identity "decrypt(encrypt(data)) == data == verify(sign(data))". + * + * @return this + */ + fun applyCRLFEncoding() = apply { + applyCRLFEncoding = true + } + + /** + * Return the input encoding that will be applied before signing / encryption. + * + * @return input encoding + */ + val isApplyCRLFEncoding: Boolean + get() = applyCRLFEncoding + + /** + * Override which compression algorithm shall be used. + * + * @param compressionAlgorithm compression algorithm override + * @return builder + */ + fun overrideCompressionAlgorithm(compressionAlgorithm: CompressionAlgorithm) = apply { + _compressionAlgorithmOverride = compressionAlgorithm + } + + val compressionAlgorithmOverride: CompressionAlgorithm + get() = _compressionAlgorithmOverride + + val isHideArmorHeaders: Boolean + get() = _hideArmorHeaders + + /** + * If set to `true`, armor headers like version or comments will be omitted from armored output. + * By default, armor headers are not hidden. + * Note: If comments are added via [setComment], those are not omitted, even if + * [hideArmorHeaders] is set to `true`. + * + * @param hideArmorHeaders true or false + * @return this + */ + fun setHideArmorHeaders(hideArmorHeaders: Boolean) = apply { + _hideArmorHeaders = hideArmorHeaders + } + + companion object { + /** + * Sign and encrypt some data. + * + * @param encryptionOptions encryption options + * @param signingOptions signing options + * @return builder + */ + @JvmStatic + fun signAndEncrypt(encryptionOptions: EncryptionOptions, signingOptions: SigningOptions) = + ProducerOptions(encryptionOptions, signingOptions) + + /** + * Sign some data without encryption. + * + * @param signingOptions signing options + * @return builder + */ + @JvmStatic + fun sign(signingOptions: SigningOptions) = ProducerOptions(null, signingOptions) + + /** + * Encrypt some data without signing. + * + * @param encryptionOptions encryption options + * @return builder + */ + @JvmStatic + fun encrypt(encryptionOptions: EncryptionOptions) = ProducerOptions(encryptionOptions, null) + + /** + * Only wrap the data in an OpenPGP packet. + * No encryption or signing will be applied. + * + * @return builder + */ + @JvmStatic + fun noEncryptionNoSigning() = ProducerOptions(null, null) + } +} \ No newline at end of file