1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-11-19 02:42:05 +01:00

Kotlin conversion: ArmoredOutputStreamFactory

Also allow configuration of CRC calculation for both input and output streams
This commit is contained in:
Paul Schaub 2023-09-29 14:57:48 +02:00
parent e16376ca68
commit aca884e936
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
5 changed files with 114 additions and 134 deletions

View file

@ -1,132 +0,0 @@
// SPDX-FileCopyrightText: 2021 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.util;
import java.io.OutputStream;
import org.bouncycastle.bcpg.ArmoredOutputStream;
import org.pgpainless.encryption_signing.ProducerOptions;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Factory to create configured {@link ArmoredOutputStream ArmoredOutputStreams}.
* The configuration entails setting custom version and comment headers.
*/
public final class ArmoredOutputStreamFactory {
/**
* Name of the program.
*/
public static final String PGPAINLESS = "PGPainless";
private static String version = PGPAINLESS;
private static String[] comment = new String[0];
private ArmoredOutputStreamFactory() {
}
private static ArmoredOutputStream.Builder getBuilder() {
ArmoredOutputStream.Builder builder = ArmoredOutputStream.builder();
builder.clearHeaders();
if (version != null && !version.isEmpty()) {
builder.setVersion(version);
}
for (String comment : comment) {
builder.addComment(comment);
}
return builder;
}
/**
* Wrap an {@link OutputStream} inside a preconfigured {@link ArmoredOutputStream}.
*
* @param outputStream inner stream
* @return armored output stream
*/
@Nonnull
public static ArmoredOutputStream get(@Nonnull OutputStream outputStream) {
return getBuilder().build(outputStream);
}
/**
* Return an instance of the {@link ArmoredOutputStream} which might have pre-populated armor headers.
*
* @param outputStream output stream
* @param options options
* @return armored output stream
*/
@Nonnull
public static ArmoredOutputStream get(@Nonnull OutputStream outputStream, @Nonnull ProducerOptions options) {
ArmoredOutputStream.Builder builder = getBuilder();
if (options.isHideArmorHeaders()) {
builder.clearHeaders();
}
if (options.hasVersion()) {
builder.setVersion(options.getVersion());
}
if (options.hasComment()) {
builder.setComment(options.getComment());
}
return builder.build(outputStream);
}
/**
* Overwrite the version header of ASCII armors with a custom value.
* Newlines in the version info string result in multiple version header entries.
* If this is set to <pre>null</pre>, then the version header is omitted altogether.
*
* @param versionString version string
*/
public static void setVersionInfo(@Nullable String versionString) {
if (versionString == null) {
version = null;
return;
}
String trimmed = versionString.trim();
if (trimmed.isEmpty()) {
version = null;
} else {
version = trimmed;
}
}
/**
* Reset the version header to its default value of {@link #PGPAINLESS}.
*/
public static void resetVersionInfo() {
version = PGPAINLESS;
}
/**
* Set a comment header value in the ASCII armor header.
* If the comment contains newlines, it will be split into multiple header entries.
*
* @see org.pgpainless.encryption_signing.ProducerOptions#setComment(String) for how to set comments for
* individual messages.
*
* @param commentString comment
*/
public static void setComment(@Nullable String commentString) {
if (commentString == null) {
throw new IllegalArgumentException("Comment cannot be null.");
}
String trimmed = commentString.trim();
if (trimmed.isEmpty()) {
throw new IllegalArgumentException("Comment cannot be empty.");
}
String[] lines = commentString.split("\n");
comment = lines;
}
/**
* Reset to the default of no comment headers.
*/
public static void resetComment() {
comment = new String[0];
}
}

View file

@ -24,6 +24,7 @@ import java.util.*
class ConsumerOptions {
private var ignoreMDCErrors = false
var isDisableAsciiArmorCRC = false
private var forceNonOpenPgpData = false
private var verifyNotBefore: Date? = null
private var verifyNotAfter: Date? = Date()

View file

@ -20,6 +20,7 @@ class ProducerOptions private constructor(
private var applyCRLFEncoding = false
private var cleartextSigned = false
private var _hideArmorHeaders = false
var isDisableAsciiArmorCRC = false
private var _compressionAlgorithmOverride: CompressionAlgorithm = PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm
private var asciiArmor = true

View file

@ -5,6 +5,7 @@
package org.pgpainless.util
import org.bouncycastle.bcpg.ArmoredInputStream
import org.pgpainless.decryption_verification.ConsumerOptions
import java.io.IOException
import java.io.InputStream
@ -24,12 +25,19 @@ class ArmoredInputStreamFactory {
* @throws IOException in case of an IO error
*/
@JvmStatic
@JvmOverloads
@Throws(IOException::class)
fun get(inputStream: InputStream): ArmoredInputStream {
fun get(inputStream: InputStream, options: ConsumerOptions? = null): ArmoredInputStream {
return when (inputStream) {
is CRCingArmoredInputStreamWrapper -> inputStream
is ArmoredInputStream -> CRCingArmoredInputStreamWrapper(inputStream)
else -> CRCingArmoredInputStreamWrapper(ArmoredInputStream(inputStream))
else -> CRCingArmoredInputStreamWrapper(
ArmoredInputStream.builder().apply {
setParseForHeaders(true)
options?.let {
setIgnoreCRC(it.isDisableAsciiArmorCRC)
}
}.build(inputStream))
}
}
}

View file

@ -0,0 +1,102 @@
// SPDX-FileCopyrightText: 2023 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.util
import org.bouncycastle.bcpg.ArmoredOutputStream
import org.pgpainless.encryption_signing.ProducerOptions
import java.io.OutputStream
/**
* Factory to create configured [ArmoredOutputStream] instances.
* The configuration entails setting custom version and comment headers.
*/
class ArmoredOutputStreamFactory {
companion object {
private const val PGPAINLESS = "PGPainless"
@JvmStatic
private var version: String? = PGPAINLESS
private var comment: String? = null
/**
* Return an instance of the [ArmoredOutputStream] which might have pre-populated armor headers.
*
* @param outputStream output stream
* @param options options
* @return armored output stream
*/
@JvmStatic
@JvmOverloads
fun get(outputStream: OutputStream, options: ProducerOptions? = null): ArmoredOutputStream {
val builder = ArmoredOutputStream.builder().apply {
// set fields defined in ArmoredOutputStreamFactory
if (!version.isNullOrBlank()) setVersion(version)
if (!comment.isNullOrBlank()) setComment(comment)
// set (and potentially overwrite with) values from ProducerOptions
options?.let {
enableCRC(!it.isDisableAsciiArmorCRC)
if (it.isHideArmorHeaders) clearHeaders()
if (it.hasVersion()) setVersion(it.version)
if (it.hasComment()) addComment(it.comment)
// TODO: configure CRC
}
}
return get(outputStream, builder)
}
/**
* Build an [ArmoredOutputStream] around the given [outputStream], configured according to the passed in
* [ArmoredOutputStream.Builder] instance.
*
* @param outputStream output stream
* @param builder builder instance
*/
@JvmStatic
fun get(outputStream: OutputStream, builder: ArmoredOutputStream.Builder): ArmoredOutputStream {
return builder.build(outputStream)
}
/**
* Overwrite the version header of ASCII armors with a custom value.
* Newlines in the version info string result in multiple version header entries.
* If this is set to <pre>null</pre>, then the version header is omitted altogether.
*
* @param versionString version string
*/
@JvmStatic
fun setVersionInfo(versionString: String?) {
version = if (versionString.isNullOrBlank()) null else versionString.trim()
}
/**
* Reset the version header to its default value of [PGPAINLESS].
*/
@JvmStatic
fun resetVersionInfo() {
version = PGPAINLESS
}
/**
* Set a comment header value in the ASCII armor header.
* If the comment contains newlines, it will be split into multiple header entries.
*
* @see [ProducerOptions.setComment] for how to set comments for individual messages.
*
* @param commentString comment
*/
@JvmStatic
fun setComment(commentString: String) {
require(commentString.isNotBlank()) { "Comment cannot be empty. See resetComment() to clear the comment." }
comment = commentString.trim()
}
@JvmStatic
fun resetComment() {
comment = null
}
}
}