1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 02:09:38 +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
*/
@JvmStatic fun encryptAndOrSign() = EncryptionBuilder()
@JvmStatic fun encryptAndOrSign() = EncryptionBuilder(getInstance())
/**
* 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
import java.io.OutputStream
import org.pgpainless.PGPainless.Companion.getPolicy
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
import org.pgpainless.PGPainless
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(
outputStream: OutputStream
): EncryptionBuilderInterface.WithOptions {
return WithOptionsImpl(outputStream)
return WithOptionsImpl(outputStream, api)
}
override fun discardOutput(): EncryptionBuilderInterface.WithOptions {
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 {
return EncryptionStream(outputStream, options)
}
}
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
return EncryptionStream(outputStream, options, api)
}
}
}

View file

@ -10,11 +10,12 @@ import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing
import org.pgpainless.algorithm.EncryptionPurpose
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.algorithm.negotiation.SymmetricKeyAlgorithmNegotiator.Companion.byPopularity
import org.pgpainless.authentication.CertificateAuthority
import org.pgpainless.bouncycastle.extensions.toOpenPGPCertificate
import org.pgpainless.encryption_signing.EncryptionOptions.EncryptionKeySelector
import org.pgpainless.exception.KeyException.*
import org.pgpainless.key.SubkeyIdentifier
@ -22,7 +23,10 @@ import org.pgpainless.key.info.KeyAccessor
import org.pgpainless.key.info.KeyRingInfo
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 _encryptionKeys: MutableSet<OpenPGPComponentKey> = mutableSetOf()
private val _encryptionKeyIdentifiers: MutableSet<SubkeyIdentifier> = mutableSetOf()
@ -87,7 +91,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
.lookupByUserId(userId, email, evaluationDate, targetAmount)
.filter { it.isAuthenticated() }
.forEach {
addRecipient(it.certificate.toOpenPGPCertificate()).also { foundAcceptable = true }
addRecipient(api.toCertificate(it.certificate)).also { foundAcceptable = true }
}
require(foundAcceptable) {
"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,
userId: CharSequence,
encryptionKeySelector: EncryptionKeySelector
) = addRecipient(key.toOpenPGPCertificate(), userId, encryptionKeySelector)
) = addRecipient(api.toCertificate(key), userId, encryptionKeySelector)
/**
* Encrypt the message for the given recipients [OpenPGPCertificate], filtering encryption
@ -251,7 +255,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
replaceWith =
ReplaceWith("addRecipient(key.toOpenPGPCertificate(), 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
@ -282,7 +286,7 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
fun addHiddenRecipient(
key: PGPPublicKeyRing,
selector: EncryptionKeySelector = encryptionKeySelector
) = addHiddenRecipient(key.toOpenPGPCertificate(), selector)
) = addHiddenRecipient(api.toCertificate(key), selector)
private fun addAsRecipient(
cert: OpenPGPCertificate,
@ -406,6 +410,17 @@ class EncryptionOptions(private val purpose: EncryptionPurpose) {
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 selectEncryptionSubkeys(
encryptionCapableKeys: List<OpenPGPComponentKey>

View file

@ -13,13 +13,12 @@ import org.bouncycastle.openpgp.PGPCompressedDataGenerator
import org.bouncycastle.openpgp.PGPEncryptedDataGenerator
import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPLiteralDataGenerator
import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.pgpainless.PGPainless
import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.key.SubkeyIdentifier
import org.pgpainless.util.ArmoredOutputStreamFactory
import org.slf4j.LoggerFactory
// 1 << 8 causes wrong partial body length encoding
// 1 << 9 fixes this.
@ -38,6 +37,7 @@ const val BUFFER_SIZE = 1 shl 9
class EncryptionStream(
private var outermostStream: OutputStream,
private val options: ProducerOptions,
private val api: PGPainless
) : OutputStream() {
private val resultBuilder: EncryptionResult.Builder = EncryptionResult.builder()
@ -63,12 +63,10 @@ class EncryptionStream(
private fun prepareArmor() {
if (!options.isAsciiArmor) {
LOGGER.debug("Output will be unarmored.")
return
}
outermostStream = BufferedOutputStream(outermostStream)
LOGGER.debug("Wrap encryption output in ASCII armor.")
armorOutputStream =
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."
}
EncryptionBuilder.negotiateSymmetricEncryptionAlgorithm(options.encryptionOptions).let {
options.encryptionOptions.negotiateSymmetricEncryptionAlgorithm().let {
resultBuilder.setEncryptionAlgorithm(it)
LOGGER.debug("Encrypt message using symmetric algorithm $it.")
val encryptedDataGenerator =
PGPEncryptedDataGenerator(
OpenPGPImplementation.getInstance()
.pgpDataEncryptorBuilder(it.algorithmId)
.apply { setWithIntegrityPacket(true) })
api.implementation.pgpDataEncryptorBuilder(it.algorithmId).apply {
setWithIntegrityPacket(true)
})
options.encryptionOptions.encryptionMethods.forEach { m ->
encryptedDataGenerator.addMethod(m)
}
@ -109,12 +106,11 @@ class EncryptionStream(
@Throws(IOException::class)
private fun prepareCompression() {
EncryptionBuilder.negotiateCompressionAlgorithm(options).let {
options.negotiateCompressionAlgorithm().let {
resultBuilder.setCompressionAlgorithm(it)
compressedDataGenerator = PGPCompressedDataGenerator(it.algorithmId)
if (it == CompressionAlgorithm.UNCOMPRESSED) return
LOGGER.debug("Compress using $it.")
basicCompressionStream =
BCPGOutputStream(compressedDataGenerator!!.open(outermostStream)).also { stream ->
outermostStream = stream
@ -267,8 +263,4 @@ class EncryptionStream(
val isClosed
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.StreamEncoding
class ProducerOptions
private constructor(
class ProducerOptions(
val encryptionOptions: EncryptionOptions?,
val signingOptions: SigningOptions?
val signingOptions: SigningOptions?,
val api: PGPainless = PGPainless.getInstance()
) {
private var _fileName: String = ""
@ -25,7 +25,7 @@ private constructor(
var isDisableAsciiArmorCRC = false
private var _compressionAlgorithmOverride: CompressionAlgorithm =
PGPainless.getPolicy().compressionAlgorithmPolicy.defaultCompressionAlgorithm
api.algorithmPolicy.compressionAlgorithmPolicy.defaultCompressionAlgorithm
private var asciiArmor = true
private var _comment: String? = null
private var _version: String? = null
@ -104,6 +104,13 @@ private constructor(
*/
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 {
require(signingOptions != null) {
"Signing Options cannot be null if cleartext signing is enabled."
@ -230,6 +237,10 @@ private constructor(
_hideArmorHeaders = hideArmorHeaders
}
internal fun negotiateCompressionAlgorithm(): CompressionAlgorithm {
return compressionAlgorithmOverride
}
companion object {
/**
* Sign and encrypt some data.

View file

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