1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 10:19:39 +02:00

More code cleanup

This commit is contained in:
Paul Schaub 2025-03-18 11:16:37 +01:00
parent 62f3a35c02
commit 1967483984
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 51 additions and 74 deletions

View file

@ -190,7 +190,7 @@ class PGPainless(
* *
* @return builder * @return builder
*/ */
@JvmStatic fun encryptAndOrSign() = EncryptionBuilder() @JvmStatic fun encryptAndOrSign() = EncryptionBuilder(getInstance())
/** /**
* Create a [DecryptionBuilder], which can be used to decrypt and/or verify data using * Create a [DecryptionBuilder], which can be used to decrypt and/or verify data using

View file

@ -5,65 +5,25 @@
package org.pgpainless.encryption_signing package org.pgpainless.encryption_signing
import java.io.OutputStream import java.io.OutputStream
import org.pgpainless.PGPainless.Companion.getPolicy import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
import org.pgpainless.util.NullOutputStream import org.pgpainless.util.NullOutputStream
import org.slf4j.Logger
import org.slf4j.LoggerFactory
class EncryptionBuilder : EncryptionBuilderInterface { class EncryptionBuilder(private val api: PGPainless) : EncryptionBuilderInterface {
override fun onOutputStream( override fun onOutputStream(
outputStream: OutputStream outputStream: OutputStream
): EncryptionBuilderInterface.WithOptions { ): EncryptionBuilderInterface.WithOptions {
return WithOptionsImpl(outputStream) return WithOptionsImpl(outputStream, api)
} }
override fun discardOutput(): EncryptionBuilderInterface.WithOptions { override fun discardOutput(): EncryptionBuilderInterface.WithOptions {
return onOutputStream(NullOutputStream()) return onOutputStream(NullOutputStream())
} }
class WithOptionsImpl(val outputStream: OutputStream) : EncryptionBuilderInterface.WithOptions { class WithOptionsImpl(val outputStream: OutputStream, private val api: PGPainless) :
EncryptionBuilderInterface.WithOptions {
override fun withOptions(options: ProducerOptions): EncryptionStream { override fun withOptions(options: ProducerOptions): EncryptionStream {
return EncryptionStream(outputStream, options) return EncryptionStream(outputStream, options, api)
}
}
companion object {
@JvmStatic val LOGGER: Logger = LoggerFactory.getLogger(EncryptionBuilder::class.java)
/**
* Negotiate the [SymmetricKeyAlgorithm] used for message encryption.
*
* @param encryptionOptions encryption options
* @return negotiated symmetric key algorithm
*/
@JvmStatic
fun negotiateSymmetricEncryptionAlgorithm(
encryptionOptions: EncryptionOptions
): SymmetricKeyAlgorithm {
val preferences =
encryptionOptions.keyViews.values
.map { it.preferredSymmetricKeyAlgorithms }
.toList()
val algorithm =
byPopularity()
.negotiate(
getPolicy().symmetricKeyEncryptionAlgorithmPolicy,
encryptionOptions.encryptionAlgorithmOverride,
preferences)
LOGGER.debug(
"Negotiation resulted in {} being the symmetric encryption algorithm of choice.",
algorithm)
return algorithm
}
@JvmStatic
fun negotiateCompressionAlgorithm(producerOptions: ProducerOptions): CompressionAlgorithm {
return producerOptions.compressionAlgorithmOverride
} }
} }
} }

View file

@ -10,11 +10,12 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.EncryptionPurpose import org.pgpainless.algorithm.EncryptionPurpose
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
import org.pgpainless.authentication.CertificateAuthority import org.pgpainless.authentication.CertificateAuthority
import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate
import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector
import org.pgpainless.exception.KeyException.* import org.pgpainless.exception.KeyException.*
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
@ -22,7 +23,10 @@ import org.pgpainless.key.info.KeyAccessor
import org.pgpainless.key.info.KeyRingInfo import org.pgpainless.key.info.KeyRingInfo
import org.pgpainless.util.Passphrase import org.pgpainless.util.Passphrase
class EncryptionOptions(private val purpose: EncryptionPurpose) { class EncryptionOptions(
private val purpose: EncryptionPurpose,
private val api: PGPainless = PGPainless.getInstance()
) {
private val _encryptionMethods: MutableSet<PGPKeyEncryptionMethodGenerator> = mutableSetOf() private val _encryptionMethods: MutableSet<PGPKeyEncryptionMethodGenerator> = mutableSetOf()
private val _encryptionKeys: MutableSet<OpenPGPComponentKey> = mutableSetOf() private val _encryptionKeys: MutableSet<OpenPGPComponentKey> = mutableSetOf()
private val _encryptionKeyIdentifiers: MutableSet<SubkeyIdentifier> = mutableSetOf() private val _encryptionKeyIdentifiers: MutableSet<SubkeyIdentifier> = mutableSetOf()
@ -87,7 +91,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
.lookupByUserId(userId, email, evaluationDate, targetAmount) .lookupByUserId(userId, email, evaluationDate, targetAmount)
.filter { it.isAuthenticated() } .filter { it.isAuthenticated() }
.forEach { .forEach {
addRecipient(it.certificate.toOpenPGPCertificate()).also { foundAcceptable = true } addRecipient(api.toCertificate(it.certificate)).also { foundAcceptable = true }
} }
require(foundAcceptable) { require(foundAcceptable) {
"Could not identify any trust-worthy certificates for '$userId' and target trust amount $targetAmount." "Could not identify any trust-worthy certificates for '$userId' and target trust amount $targetAmount."
@ -225,7 +229,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
key: PGPPublicKeyRing, key: PGPPublicKeyRing,
userId: CharSequence, userId: CharSequence,
encryptionKeySelector: EncryptionKeySelector encryptionKeySelector: EncryptionKeySelector
) = addRecipient(key.toOpenPGPCertificate(), userId, encryptionKeySelector) ) = addRecipient(api.toCertificate(key), userId, encryptionKeySelector)
/** /**
* Encrypt the message for the given recipients [OpenPGPCertificate], filtering encryption * Encrypt the message for the given recipients [OpenPGPCertificate], filtering encryption
@ -251,7 +255,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
replaceWith = replaceWith =
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)")) ReplaceWith("addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector)"))
fun addRecipient(key: PGPPublicKeyRing, encryptionKeySelector: EncryptionKeySelector) = fun addRecipient(key: PGPPublicKeyRing, encryptionKeySelector: EncryptionKeySelector) =
addRecipient(key.toOpenPGPCertificate(), encryptionKeySelector) addRecipient(api.toCertificate(key), encryptionKeySelector)
/** /**
* Encrypt the message for the recipients [OpenPGPCertificate], keeping the recipient anonymous * Encrypt the message for the recipients [OpenPGPCertificate], keeping the recipient anonymous
@ -282,7 +286,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
fun addHiddenRecipient( fun addHiddenRecipient(
key: PGPPublicKeyRing, key: PGPPublicKeyRing,
selector: EncryptionKeySelector = encryptionKeySelector selector: EncryptionKeySelector = encryptionKeySelector
) = addHiddenRecipient(key.toOpenPGPCertificate(), selector) ) = addHiddenRecipient(api.toCertificate(key), selector)
private fun addAsRecipient( private fun addAsRecipient(
cert: OpenPGPCertificate, cert: OpenPGPCertificate,
@ -406,6 +410,17 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
fun hasEncryptionMethod() = _encryptionMethods.isNotEmpty() fun hasEncryptionMethod() = _encryptionMethods.isNotEmpty()
internal fun negotiateSymmetricEncryptionAlgorithm(): SymmetricKeyAlgorithm {
val preferences = keyViews.values.map { it.preferredSymmetricKeyAlgorithms }.toList()
val algorithm =
byPopularity()
.negotiate(
api.algorithmPolicy.symmetricKeyEncryptionAlgorithmPolicy,
encryptionAlgorithmOverride,
preferences)
return algorithm
}
fun interface EncryptionKeySelector { fun interface EncryptionKeySelector {
fun selectEncryptionSubkeys( fun selectEncryptionSubkeys(
encryptionCapableKeys: List<OpenPGPComponentKey> encryptionCapableKeys: List<OpenPGPComponentKey>

View file

@ -13,13 +13,12 @@ import org.bouncycastle.openpgp.PGPCompressedDataGenerator
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator import org.bouncycastle.openpgp.PGPEncryptedDataGenerator
import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPLiteralDataGenerator import org.bouncycastle.openpgp.PGPLiteralDataGenerator
import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.util.ArmoredOutputStreamFactory import org.pgpainless.util.ArmoredOutputStreamFactory
import org.slf4j.LoggerFactory
// 1 << 8 causes wrong partial body length encoding // 1 << 8 causes wrong partial body length encoding
// 1 << 9 fixes this. // 1 << 9 fixes this.
@ -38,6 +37,7 @@ const val BUFFER_SIZE = 1 shl 9
class EncryptionStream( class EncryptionStream(
private var outermostStream: OutputStream, private var outermostStream: OutputStream,
private val options: ProducerOptions, private val options: ProducerOptions,
private val api: PGPainless
) : OutputStream() { ) : OutputStream() {
private val resultBuilder: EncryptionResult.Builder = EncryptionResult.builder() private val resultBuilder: EncryptionResult.Builder = EncryptionResult.builder()
@ -63,12 +63,10 @@ class EncryptionStream(
private fun prepareArmor() { private fun prepareArmor() {
if (!options.isAsciiArmor) { if (!options.isAsciiArmor) {
LOGGER.debug("Output will be unarmored.")
return return
} }
outermostStream = BufferedOutputStream(outermostStream) outermostStream = BufferedOutputStream(outermostStream)
LOGGER.debug("Wrap encryption output in ASCII armor.")
armorOutputStream = armorOutputStream =
ArmoredOutputStreamFactory.get(outermostStream, options).also { outermostStream = it } ArmoredOutputStreamFactory.get(outermostStream, options).also { outermostStream = it }
} }
@ -84,14 +82,13 @@ class EncryptionStream(
"If EncryptionOptions are provided, at least one encryption method MUST be provided as well." "If EncryptionOptions are provided, at least one encryption method MUST be provided as well."
} }
EncryptionBuilder.negotiateSymmetricEncryptionAlgorithm(options.encryptionOptions).let { options.encryptionOptions.negotiateSymmetricEncryptionAlgorithm().let {
resultBuilder.setEncryptionAlgorithm(it) resultBuilder.setEncryptionAlgorithm(it)
LOGGER.debug("Encrypt message using symmetric algorithm $it.")
val encryptedDataGenerator = val encryptedDataGenerator =
PGPEncryptedDataGenerator( PGPEncryptedDataGenerator(
OpenPGPImplementation.getInstance() api.implementation.pgpDataEncryptorBuilder(it.algorithmId).apply {
.pgpDataEncryptorBuilder(it.algorithmId) setWithIntegrityPacket(true)
.apply { setWithIntegrityPacket(true) }) })
options.encryptionOptions.encryptionMethods.forEach { m -> options.encryptionOptions.encryptionMethods.forEach { m ->
encryptedDataGenerator.addMethod(m) encryptedDataGenerator.addMethod(m)
} }
@ -109,12 +106,11 @@ class EncryptionStream(
@Throws(IOException::class) @Throws(IOException::class)
private fun prepareCompression() { private fun prepareCompression() {
EncryptionBuilder.negotiateCompressionAlgorithm(options).let { options.negotiateCompressionAlgorithm().let {
resultBuilder.setCompressionAlgorithm(it) resultBuilder.setCompressionAlgorithm(it)
compressedDataGenerator = PGPCompressedDataGenerator(it.algorithmId) compressedDataGenerator = PGPCompressedDataGenerator(it.algorithmId)
if (it == CompressionAlgorithm.UNCOMPRESSED) return if (it == CompressionAlgorithm.UNCOMPRESSED) return
LOGGER.debug("Compress using $it.")
basicCompressionStream = basicCompressionStream =
BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)).also { stream -> BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)).also { stream ->
outermostStream = stream outermostStream = stream
@ -267,8 +263,4 @@ class EncryptionStream(
val isClosed val isClosed
get() = closed get() = closed
companion object {
@JvmStatic private val LOGGER = LoggerFactory.getLogger(EncryptionStream::class.java)
}
} }

View file

@ -10,10 +10,10 @@ import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.algorithm.StreamEncoding
class ProducerOptions class ProducerOptions(
private constructor(
val encryptionOptions: EncryptionOptions?, val encryptionOptions: EncryptionOptions?,
val signingOptions: SigningOptions? val signingOptions: SigningOptions?,
val api: PGPainless = PGPainless.getInstance()
) { ) {
private var _fileName: String = "" private var _fileName: String = ""
@ -25,7 +25,7 @@ private constructor(
var isDisableAsciiArmorCRC = false var isDisableAsciiArmorCRC = false
private var _compressionAlgorithmOverride: CompressionAlgorithm = private var _compressionAlgorithmOverride: CompressionAlgorithm =
PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm api.algorithmPolicy.compressionAlgorithmPolicy.defaultCompressionAlgorithm
private var asciiArmor = true private var asciiArmor = true
private var _comment: String? = null private var _comment: String? = null
private var _version: String? = null private var _version: String? = null
@ -104,6 +104,13 @@ private constructor(
*/ */
fun hasVersion() = version != null fun hasVersion() = version != null
/**
* Configure the resulting OpenPGP message to make use of the Cleartext Signature Framework
* (CSF). A CSF message MUST be signed using detached signatures only and MUST NOT be encrypted.
*
* @see
* [RFC9580: OpenPGP - Cleartext Signature Framework](https://www.rfc-editor.org/rfc/rfc9580.html#name-cleartext-signature-framewo)
*/
fun setCleartextSigned() = apply { fun setCleartextSigned() = apply {
require(signingOptions != null) { require(signingOptions != null) {
"Signing Options cannot be null if cleartext signing is enabled." "Signing Options cannot be null if cleartext signing is enabled."
@ -230,6 +237,10 @@ private constructor(
_hideArmorHeaders = hideArmorHeaders _hideArmorHeaders = hideArmorHeaders
} }
internal fun negotiateCompressionAlgorithm(): CompressionAlgorithm {
return compressionAlgorithmOverride
}
companion object { companion object {
/** /**
* Sign and encrypt some data. * Sign and encrypt some data.

View file

@ -17,7 +17,6 @@ import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.algorithm.EncryptionPurpose;
import org.pgpainless.encryption_signing.EncryptionOptions; import org.pgpainless.encryption_signing.EncryptionOptions;
import org.pgpainless.encryption_signing.EncryptionResult; import org.pgpainless.encryption_signing.EncryptionResult;
import org.pgpainless.encryption_signing.EncryptionStream; import org.pgpainless.encryption_signing.EncryptionStream;
@ -51,7 +50,7 @@ public class TestTwoSubkeysEncryption {
EncryptionStream encryptionStream = PGPainless.encryptAndOrSign() EncryptionStream encryptionStream = PGPainless.encryptAndOrSign()
.onOutputStream(out) .onOutputStream(out)
.withOptions( .withOptions(
ProducerOptions.encrypt(new EncryptionOptions(EncryptionPurpose.ANY) ProducerOptions.encrypt(EncryptionOptions.get()
.addRecipient(publicKeys, EncryptionOptions.encryptToAllCapableSubkeys()) .addRecipient(publicKeys, EncryptionOptions.encryptToAllCapableSubkeys())
) )
.setAsciiArmor(false) .setAsciiArmor(false)