From 35b0cdde7e75f1005ef80f567093cc7f02e0f3d7 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 18 Mar 2025 13:52:19 +0100 Subject: [PATCH] More API down-handing --- .../main/kotlin/org/pgpainless/PGPainless.kt | 2 +- .../DecryptionBuilder.kt | 10 +- .../MessageMetadata.kt | 15 ++- .../OpenPgpMessageInputStream.kt | 103 +++++++----------- .../encryption_signing/EncryptionOptions.kt | 10 +- .../OpenPgpMessageInputStreamTest.java | 2 +- 6 files changed, 62 insertions(+), 80 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt index 41aa437c..fc3b182b 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/PGPainless.kt @@ -217,7 +217,7 @@ class PGPainless( * * @return builder */ - @JvmStatic fun decryptAndOrVerify(): DecryptionBuilder = DecryptionBuilder() + @JvmStatic fun decryptAndOrVerify(): DecryptionBuilder = DecryptionBuilder(getInstance()) /** * Make changes to a secret key at the given reference time. This method can be used to diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt index d1d4f8b2..147fa62a 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/DecryptionBuilder.kt @@ -5,22 +5,24 @@ package org.pgpainless.decryption_verification import java.io.InputStream +import org.pgpainless.PGPainless /** * Builder class that takes an [InputStream] of ciphertext (or plaintext signed data) and combines * it with a configured [ConsumerOptions] object to form a [DecryptionStream] which can be used to * decrypt an OpenPGP message or verify signatures. */ -class DecryptionBuilder : DecryptionBuilderInterface { +class DecryptionBuilder(private val api: PGPainless) : DecryptionBuilderInterface { override fun onInputStream(inputStream: InputStream): DecryptionBuilderInterface.DecryptWith { - return DecryptWithImpl(inputStream) + return DecryptWithImpl(inputStream, api) } - class DecryptWithImpl(val inputStream: InputStream) : DecryptionBuilderInterface.DecryptWith { + class DecryptWithImpl(val inputStream: InputStream, val api: PGPainless) : + DecryptionBuilderInterface.DecryptWith { override fun withOptions(consumerOptions: ConsumerOptions): DecryptionStream { - return OpenPgpMessageInputStream.create(inputStream, consumerOptions) + return OpenPgpMessageInputStream.create(inputStream, consumerOptions, api) } } } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt index 79a0ca98..3a62bc72 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/MessageMetadata.kt @@ -51,7 +51,7 @@ class MessageMetadata(val message: Message) { fun isEncryptedFor(cert: OpenPGPCertificate): Boolean { return encryptionLayers.asSequence().any { - it.recipients.any { keyId -> cert.getKey(KeyIdentifier(keyId)) != null } + it.recipients.any { identifier -> cert.getKey(identifier) != null } } } @@ -87,12 +87,15 @@ class MessageMetadata(val message: Message) { /** List containing all recipient keyIDs. */ val recipientKeyIds: List + get() = recipientKeyIdentifiers.map { it.keyId }.toList() + + val recipientKeyIdentifiers: List get() = encryptionLayers .asSequence() .map { it.recipients.toMutableList() } - .reduce { all, keyIds -> - all.addAll(keyIds) + .reduce { all, keyIdentifiers -> + all.addAll(keyIdentifiers) all } .toList() @@ -475,9 +478,11 @@ class MessageMetadata(val message: Message) { var sessionKey: SessionKey? = null /** List of all recipient key ids to which the packet was encrypted for. */ - val recipients: List = mutableListOf() + val recipients: List = mutableListOf() - fun addRecipients(keyIds: List) = apply { (recipients as MutableList).addAll(keyIds) } + fun addRecipients(keyIds: List) = apply { + (recipients as MutableList).addAll(keyIds) + } /** * Identifier of the subkey that was used to decrypt the packet (in case of a public key diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt index 756d326a..904ba78e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt @@ -26,7 +26,6 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData import org.bouncycastle.openpgp.PGPSessionKey import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.api.OpenPGPCertificate -import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey @@ -74,10 +73,10 @@ class OpenPgpMessageInputStream( inputStream: InputStream, private val options: ConsumerOptions, private val layerMetadata: Layer, - private val policy: Policy + private val api: PGPainless ) : DecryptionStream() { - private val signatures: Signatures = Signatures(options) + private val signatures: Signatures = Signatures(options, api) private var packetInputStream: TeeBCPGInputStream? = null private var nestedInputStream: InputStream? = null private val syntaxVerifier = PDA() @@ -131,8 +130,8 @@ class OpenPgpMessageInputStream( inputStream: InputStream, options: ConsumerOptions, metadata: Layer, - policy: Policy - ) : this(Type.standard, inputStream, options, metadata, policy) + api: PGPainless + ) : this(Type.standard, inputStream, options, metadata, api) private fun consumePackets() { val pIn = packetInputStream ?: return @@ -232,7 +231,7 @@ class OpenPgpMessageInputStream( LOGGER.debug( "Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.") nestedInputStream = - OpenPgpMessageInputStream(decompress(compressedData), options, compressionLayer, policy) + OpenPgpMessageInputStream(decompress(compressedData), options, compressionLayer, api) } private fun decompress(compressedData: PGPCompressedData): InputStream { @@ -313,7 +312,8 @@ class OpenPgpMessageInputStream( signatures .leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are // dealt with - signatures.addCorrespondingOnePassSignature(signature, layerMetadata, policy) + signatures.addCorrespondingOnePassSignature( + signature, layerMetadata, api.algorithmPolicy) } else { LOGGER.debug( "Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") @@ -345,7 +345,7 @@ class OpenPgpMessageInputStream( esks.pkesks .filter { // find matching PKESK - it.keyID == key.subkeyId + it.keyIdentifier == key.keyIdentifier } .forEach { // attempt decryption @@ -362,8 +362,7 @@ class OpenPgpMessageInputStream( throwIfUnacceptable(sk.algorithm) val pgpSk = PGPSessionKey(sk.algorithm.algorithmId, sk.key) - val decryptorFactory = - OpenPGPImplementation.getInstance().sessionKeyDataDecryptorFactory(pgpSk) + val decryptorFactory = api.implementation.sessionKeyDataDecryptorFactory(pgpSk) val layer = EncryptedData(sk.algorithm, layerMetadata.depth + 1) val skEncData = encDataList.extractSessionKeyEncryptedData() try { @@ -372,7 +371,7 @@ class OpenPgpMessageInputStream( val integrityProtected = IntegrityProtectedInputStream(decrypted, skEncData, options) nestedInputStream = - OpenPgpMessageInputStream(integrityProtected, options, layer, policy) + OpenPgpMessageInputStream(integrityProtected, options, layer, api) LOGGER.debug("Successfully decrypted data using provided session key") return true } catch (e: PGPException) { @@ -395,8 +394,7 @@ class OpenPgpMessageInputStream( } val decryptorFactory = - OpenPGPImplementation.getInstance() - .pbeDataDecryptorFactory(passphrase.getChars()) + api.implementation.pbeDataDecryptorFactory(passphrase.getChars()) if (decryptSKESKAndStream(esks, skesk, decryptorFactory)) { return true } @@ -518,7 +516,7 @@ class OpenPgpMessageInputStream( pkesk: PGPPublicKeyEncryptedData ): Boolean { val decryptorFactory = - OpenPGPImplementation.getInstance().publicKeyDataDecryptorFactory(privateKey.privateKey) + api.implementation.publicKeyDataDecryptorFactory(privateKey.privateKey) return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk) } @@ -545,11 +543,11 @@ class OpenPgpMessageInputStream( throwIfUnacceptable(sessionKey.algorithm) val encryptedData = EncryptedData(sessionKey.algorithm, layerMetadata.depth + 1) encryptedData.sessionKey = sessionKey - encryptedData.addRecipients(esks.pkesks.map { it.keyID }) + encryptedData.addRecipients(esks.pkesks.map { it.keyIdentifier }) LOGGER.debug("Successfully decrypted data with passphrase") val integrityProtected = IntegrityProtectedInputStream(decrypted, skesk, options) nestedInputStream = - OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) + OpenPgpMessageInputStream(integrityProtected, options, encryptedData, api) return true } catch (e: UnacceptableAlgorithmException) { throw e @@ -578,11 +576,11 @@ class OpenPgpMessageInputStream( layerMetadata.depth + 1) encryptedData.decryptionKey = decryptionKeyId encryptedData.sessionKey = sessionKey - encryptedData.addRecipients(esks.pkesks.plus(esks.anonPkesks).map { it.keyID }) + encryptedData.addRecipients(esks.pkesks.plus(esks.anonPkesks).map { it.keyIdentifier }) LOGGER.debug("Successfully decrypted data with key $decryptionKeyId") val integrityProtected = IntegrityProtectedInputStream(decrypted, pkesk, options) nestedInputStream = - OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) + OpenPgpMessageInputStream(integrityProtected, options, encryptedData, api) return true } catch (e: UnacceptableAlgorithmException) { throw e @@ -620,7 +618,7 @@ class OpenPgpMessageInputStream( throw RuntimeException(e) } } - signatures.finish(layerMetadata, policy) + signatures.finish(layerMetadata, api.algorithmPolicy) } return r } @@ -647,7 +645,7 @@ class OpenPgpMessageInputStream( throw RuntimeException(e) } } - signatures.finish(layerMetadata, policy) + signatures.finish(layerMetadata, api.algorithmPolicy) } return r } @@ -693,16 +691,6 @@ class OpenPgpMessageInputStream( return MessageMetadata((layerMetadata as Message)) } - private fun getDecryptionKey(keyId: Long): OpenPGPKey? = - options.getDecryptionKeys().firstOrNull { - it.pgpSecretKeyRing - .any { k -> k.keyID == keyId } - .and( - PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k -> - k.keyIdentifier.keyId == keyId - }) - } - private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): OpenPGPKey? = options.getDecryptionKeys().firstOrNull { it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null && @@ -737,7 +725,7 @@ class OpenPgpMessageInputStream( } private fun isAcceptable(algorithm: SymmetricKeyAlgorithm): Boolean = - policy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm) + api.algorithmPolicy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm) private fun throwIfUnacceptable(algorithm: SymmetricKeyAlgorithm) { if (!isAcceptable(algorithm)) { @@ -774,7 +762,7 @@ class OpenPgpMessageInputStream( get() = skesks.plus(pkesks).plus(anonPkesks) } - private class Signatures(val options: ConsumerOptions) : OutputStream() { + private class Signatures(val options: ConsumerOptions, val api: PGPainless) : OutputStream() { val detachedSignatures = mutableListOf() val prependedSignatures = mutableListOf() val onePassSignatures = mutableListOf() @@ -1044,27 +1032,21 @@ class OpenPgpMessageInputStream( } } - companion object { - @JvmStatic - private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) { - val verifierProvider = - OpenPGPImplementation.getInstance().pgpContentVerifierBuilderProvider() - try { - signature.init(verifierProvider, publicKey) - } catch (e: PGPException) { - throw RuntimeException(e) - } + private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) { + val verifierProvider = api.implementation.pgpContentVerifierBuilderProvider() + try { + signature.init(verifierProvider, publicKey) + } catch (e: PGPException) { + throw RuntimeException(e) } + } - @JvmStatic - private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) { - val verifierProvider = - OpenPGPImplementation.getInstance().pgpContentVerifierBuilderProvider() - try { - ops.init(verifierProvider, publicKey) - } catch (e: PGPException) { - throw RuntimeException(e) - } + private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) { + val verifierProvider = api.implementation.pgpContentVerifierBuilderProvider() + try { + ops.init(verifierProvider, publicKey) + } catch (e: PGPException) { + throw RuntimeException(e) } } } @@ -1074,32 +1056,27 @@ class OpenPgpMessageInputStream( private val LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream::class.java) @JvmStatic - fun create(inputStream: InputStream, options: ConsumerOptions) = - create(inputStream, options, PGPainless.getInstance().algorithmPolicy) - - @JvmStatic - fun create(inputStream: InputStream, options: ConsumerOptions, policy: Policy) = - create(inputStream, options, Message(), policy) + fun create(inputStream: InputStream, options: ConsumerOptions, api: PGPainless) = + create(inputStream, options, Message(), api) @JvmStatic internal fun create( inputStream: InputStream, options: ConsumerOptions, metadata: Layer, - policy: Policy + api: PGPainless ): OpenPgpMessageInputStream { val openPgpIn = OpenPgpInputStream(inputStream) openPgpIn.reset() if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) { return OpenPgpMessageInputStream( - Type.non_openpgp, openPgpIn, options, metadata, policy) + Type.non_openpgp, openPgpIn, options, metadata, api) } if (openPgpIn.isBinaryOpenPgp) { // Simply consume OpenPGP message - return OpenPgpMessageInputStream( - Type.standard, openPgpIn, options, metadata, policy) + return OpenPgpMessageInputStream(Type.standard, openPgpIn, options, metadata, api) } return if (openPgpIn.isAsciiArmored) { @@ -1107,10 +1084,10 @@ class OpenPgpMessageInputStream( if (armorIn.isClearText) { (metadata as Message).setCleartextSigned() OpenPgpMessageInputStream( - Type.cleartext_signed, armorIn, options, metadata, policy) + Type.cleartext_signed, armorIn, options, metadata, api) } else { // Simply consume dearmored OpenPGP message - OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, policy) + OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, api) } } else { throw AssertionError("Cannot deduce type of data.") diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt index b37c854f..ac975356 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/encryption_signing/EncryptionOptions.kt @@ -8,7 +8,6 @@ import java.util.* import org.bouncycastle.openpgp.PGPPublicKeyRing 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 @@ -334,9 +333,9 @@ class EncryptionOptions( _encryptionKeys.add(key) _encryptionKeyIdentifiers.add(SubkeyIdentifier(key)) addEncryptionMethod( - OpenPGPImplementation.getInstance() - .publicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey) - .also { it.setUseWildcardRecipient(wildcardRecipient) }) + api.implementation.publicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey).also { + it.setUseWildcardRecipient(wildcardRecipient) + }) } /** @@ -359,8 +358,7 @@ class EncryptionOptions( fun addMessagePassphrase(passphrase: Passphrase) = apply { require(!passphrase.isEmpty) { "Passphrase MUST NOT be empty." } addEncryptionMethod( - OpenPGPImplementation.getInstance() - .pbeKeyEncryptionMethodGenerator(passphrase.getChars())) + api.implementation.pbeKeyEncryptionMethodGenerator(passphrase.getChars())) } /** diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java index ed57fee5..20008a85 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/OpenPgpMessageInputStreamTest.java @@ -697,7 +697,7 @@ public class OpenPgpMessageInputStreamTest { throws IOException { ByteArrayInputStream bytesIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)); ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn); - OpenPgpMessageInputStream pgpIn = OpenPgpMessageInputStream.create(armorIn, options); + OpenPgpMessageInputStream pgpIn = OpenPgpMessageInputStream.create(armorIn, options, PGPainless.getInstance()); return pgpIn; } }