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

More API down-handing

This commit is contained in:
Paul Schaub 2025-03-18 13:52:19 +01:00
parent f74932c4d0
commit 7e345a0e33
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
6 changed files with 62 additions and 80 deletions

View file

@ -217,7 +217,7 @@ class PGPainless(
* *
* @return builder * @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 * Make changes to a secret key at the given reference time. This method can be used to

View file

@ -5,22 +5,24 @@
package org.pgpainless.decryption_verification package org.pgpainless.decryption_verification
import java.io.InputStream import java.io.InputStream
import org.pgpainless.PGPainless
/** /**
* Builder class that takes an [InputStream] of ciphertext (or plaintext signed data) and combines * 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 * it with a configured [ConsumerOptions] object to form a [DecryptionStream] which can be used to
* decrypt an OpenPGP message or verify signatures. * decrypt an OpenPGP message or verify signatures.
*/ */
class DecryptionBuilder : DecryptionBuilderInterface { class DecryptionBuilder(private val api: PGPainless) : DecryptionBuilderInterface {
override fun onInputStream(inputStream: InputStream): DecryptionBuilderInterface.DecryptWith { 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 { override fun withOptions(consumerOptions: ConsumerOptions): DecryptionStream {
return OpenPgpMessageInputStream.create(inputStream, consumerOptions) return OpenPgpMessageInputStream.create(inputStream, consumerOptions, api)
} }
} }
} }

View file

@ -51,7 +51,7 @@ class MessageMetadata(val message: Message) {
fun isEncryptedFor(cert: OpenPGPCertificate): Boolean { fun isEncryptedFor(cert: OpenPGPCertificate): Boolean {
return encryptionLayers.asSequence().any { 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. */ /** List containing all recipient keyIDs. */
val recipientKeyIds: List<Long> val recipientKeyIds: List<Long>
get() = recipientKeyIdentifiers.map { it.keyId }.toList()
val recipientKeyIdentifiers: List<KeyIdentifier>
get() = get() =
encryptionLayers encryptionLayers
.asSequence() .asSequence()
.map { it.recipients.toMutableList() } .map { it.recipients.toMutableList() }
.reduce { all, keyIds -> .reduce { all, keyIdentifiers ->
all.addAll(keyIds) all.addAll(keyIdentifiers)
all all
} }
.toList() .toList()
@ -475,9 +478,11 @@ class MessageMetadata(val message: Message) {
var sessionKey: SessionKey? = null var sessionKey: SessionKey? = null
/** List of all recipient key ids to which the packet was encrypted for. */ /** List of all recipient key ids to which the packet was encrypted for. */
val recipients: List<Long> = mutableListOf() val recipients: List<KeyIdentifier> = mutableListOf()
fun addRecipients(keyIds: List<Long>) = apply { (recipients as MutableList).addAll(keyIds) } fun addRecipients(keyIds: List<KeyIdentifier>) = apply {
(recipients as MutableList).addAll(keyIds)
}
/** /**
* Identifier of the subkey that was used to decrypt the packet (in case of a public key * Identifier of the subkey that was used to decrypt the packet (in case of a public key

View file

@ -26,7 +26,6 @@ import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData
import org.bouncycastle.openpgp.PGPSessionKey import org.bouncycastle.openpgp.PGPSessionKey
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPPrivateKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
@ -74,10 +73,10 @@ class OpenPgpMessageInputStream(
inputStream: InputStream, inputStream: InputStream,
private val options: ConsumerOptions, private val options: ConsumerOptions,
private val layerMetadata: Layer, private val layerMetadata: Layer,
private val policy: Policy private val api: PGPainless
) : DecryptionStream() { ) : DecryptionStream() {
private val signatures: Signatures = Signatures(options) private val signatures: Signatures = Signatures(options, api)
private var packetInputStream: TeeBCPGInputStream? = null private var packetInputStream: TeeBCPGInputStream? = null
private var nestedInputStream: InputStream? = null private var nestedInputStream: InputStream? = null
private val syntaxVerifier = PDA() private val syntaxVerifier = PDA()
@ -131,8 +130,8 @@ class OpenPgpMessageInputStream(
inputStream: InputStream, inputStream: InputStream,
options: ConsumerOptions, options: ConsumerOptions,
metadata: Layer, metadata: Layer,
policy: Policy api: PGPainless
) : this(Type.standard, inputStream, options, metadata, policy) ) : this(Type.standard, inputStream, options, metadata, api)
private fun consumePackets() { private fun consumePackets() {
val pIn = packetInputStream ?: return val pIn = packetInputStream ?: return
@ -232,7 +231,7 @@ class OpenPgpMessageInputStream(
LOGGER.debug( LOGGER.debug(
"Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.") "Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.")
nestedInputStream = nestedInputStream =
OpenPgpMessageInputStream(decompress(compressedData), options, compressionLayer, policy) OpenPgpMessageInputStream(decompress(compressedData), options, compressionLayer, api)
} }
private fun decompress(compressedData: PGPCompressedData): InputStream { private fun decompress(compressedData: PGPCompressedData): InputStream {
@ -313,7 +312,8 @@ class OpenPgpMessageInputStream(
signatures signatures
.leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are .leaveNesting() // TODO: Only leave nesting if all OPSs of the nesting layer are
// dealt with // dealt with
signatures.addCorrespondingOnePassSignature(signature, layerMetadata, policy) signatures.addCorrespondingOnePassSignature(
signature, layerMetadata, api.algorithmPolicy)
} else { } else {
LOGGER.debug( LOGGER.debug(
"Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.") "Prepended Signature Packet by key ${keyId.openPgpKeyId()} at depth ${layerMetadata.depth} encountered.")
@ -345,7 +345,7 @@ class OpenPgpMessageInputStream(
esks.pkesks esks.pkesks
.filter { .filter {
// find matching PKESK // find matching PKESK
it.keyID == key.subkeyId it.keyIdentifier == key.keyIdentifier
} }
.forEach { .forEach {
// attempt decryption // attempt decryption
@ -362,8 +362,7 @@ class OpenPgpMessageInputStream(
throwIfUnacceptable(sk.algorithm) throwIfUnacceptable(sk.algorithm)
val pgpSk = PGPSessionKey(sk.algorithm.algorithmId, sk.key) val pgpSk = PGPSessionKey(sk.algorithm.algorithmId, sk.key)
val decryptorFactory = val decryptorFactory = api.implementation.sessionKeyDataDecryptorFactory(pgpSk)
OpenPGPImplementation.getInstance().sessionKeyDataDecryptorFactory(pgpSk)
val layer = EncryptedData(sk.algorithm, layerMetadata.depth + 1) val layer = EncryptedData(sk.algorithm, layerMetadata.depth + 1)
val skEncData = encDataList.extractSessionKeyEncryptedData() val skEncData = encDataList.extractSessionKeyEncryptedData()
try { try {
@ -372,7 +371,7 @@ class OpenPgpMessageInputStream(
val integrityProtected = val integrityProtected =
IntegrityProtectedInputStream(decrypted, skEncData, options) IntegrityProtectedInputStream(decrypted, skEncData, options)
nestedInputStream = nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, layer, policy) OpenPgpMessageInputStream(integrityProtected, options, layer, api)
LOGGER.debug("Successfully decrypted data using provided session key") LOGGER.debug("Successfully decrypted data using provided session key")
return true return true
} catch (e: PGPException) { } catch (e: PGPException) {
@ -395,8 +394,7 @@ class OpenPgpMessageInputStream(
} }
val decryptorFactory = val decryptorFactory =
OpenPGPImplementation.getInstance() api.implementation.pbeDataDecryptorFactory(passphrase.getChars())
.pbeDataDecryptorFactory(passphrase.getChars())
if (decryptSKESKAndStream(esks, skesk, decryptorFactory)) { if (decryptSKESKAndStream(esks, skesk, decryptorFactory)) {
return true return true
} }
@ -518,7 +516,7 @@ class OpenPgpMessageInputStream(
pkesk: PGPPublicKeyEncryptedData pkesk: PGPPublicKeyEncryptedData
): Boolean { ): Boolean {
val decryptorFactory = val decryptorFactory =
OpenPGPImplementation.getInstance().publicKeyDataDecryptorFactory(privateKey.privateKey) api.implementation.publicKeyDataDecryptorFactory(privateKey.privateKey)
return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk) return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk)
} }
@ -545,11 +543,11 @@ class OpenPgpMessageInputStream(
throwIfUnacceptable(sessionKey.algorithm) throwIfUnacceptable(sessionKey.algorithm)
val encryptedData = EncryptedData(sessionKey.algorithm, layerMetadata.depth + 1) val encryptedData = EncryptedData(sessionKey.algorithm, layerMetadata.depth + 1)
encryptedData.sessionKey = sessionKey encryptedData.sessionKey = sessionKey
encryptedData.addRecipients(esks.pkesks.map { it.keyID }) encryptedData.addRecipients(esks.pkesks.map { it.keyIdentifier })
LOGGER.debug("Successfully decrypted data with passphrase") LOGGER.debug("Successfully decrypted data with passphrase")
val integrityProtected = IntegrityProtectedInputStream(decrypted, skesk, options) val integrityProtected = IntegrityProtectedInputStream(decrypted, skesk, options)
nestedInputStream = nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) OpenPgpMessageInputStream(integrityProtected, options, encryptedData, api)
return true return true
} catch (e: UnacceptableAlgorithmException) { } catch (e: UnacceptableAlgorithmException) {
throw e throw e
@ -578,11 +576,11 @@ class OpenPgpMessageInputStream(
layerMetadata.depth + 1) layerMetadata.depth + 1)
encryptedData.decryptionKey = decryptionKeyId encryptedData.decryptionKey = decryptionKeyId
encryptedData.sessionKey = sessionKey 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") LOGGER.debug("Successfully decrypted data with key $decryptionKeyId")
val integrityProtected = IntegrityProtectedInputStream(decrypted, pkesk, options) val integrityProtected = IntegrityProtectedInputStream(decrypted, pkesk, options)
nestedInputStream = nestedInputStream =
OpenPgpMessageInputStream(integrityProtected, options, encryptedData, policy) OpenPgpMessageInputStream(integrityProtected, options, encryptedData, api)
return true return true
} catch (e: UnacceptableAlgorithmException) { } catch (e: UnacceptableAlgorithmException) {
throw e throw e
@ -620,7 +618,7 @@ class OpenPgpMessageInputStream(
throw RuntimeException(e) throw RuntimeException(e)
} }
} }
signatures.finish(layerMetadata, policy) signatures.finish(layerMetadata, api.algorithmPolicy)
} }
return r return r
} }
@ -647,7 +645,7 @@ class OpenPgpMessageInputStream(
throw RuntimeException(e) throw RuntimeException(e)
} }
} }
signatures.finish(layerMetadata, policy) signatures.finish(layerMetadata, api.algorithmPolicy)
} }
return r return r
} }
@ -693,16 +691,6 @@ class OpenPgpMessageInputStream(
return MessageMetadata((layerMetadata as Message)) 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? = private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): OpenPGPKey? =
options.getDecryptionKeys().firstOrNull { options.getDecryptionKeys().firstOrNull {
it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null && it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null &&
@ -737,7 +725,7 @@ class OpenPgpMessageInputStream(
} }
private fun isAcceptable(algorithm: SymmetricKeyAlgorithm): Boolean = private fun isAcceptable(algorithm: SymmetricKeyAlgorithm): Boolean =
policy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm) api.algorithmPolicy.symmetricKeyDecryptionAlgorithmPolicy.isAcceptable(algorithm)
private fun throwIfUnacceptable(algorithm: SymmetricKeyAlgorithm) { private fun throwIfUnacceptable(algorithm: SymmetricKeyAlgorithm) {
if (!isAcceptable(algorithm)) { if (!isAcceptable(algorithm)) {
@ -774,7 +762,7 @@ class OpenPgpMessageInputStream(
get() = skesks.plus(pkesks).plus(anonPkesks) 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<OpenPGPDocumentSignature>() val detachedSignatures = mutableListOf<OpenPGPDocumentSignature>()
val prependedSignatures = mutableListOf<OpenPGPDocumentSignature>() val prependedSignatures = mutableListOf<OpenPGPDocumentSignature>()
val onePassSignatures = mutableListOf<OnePassSignatureCheck>() val onePassSignatures = mutableListOf<OnePassSignatureCheck>()
@ -1044,27 +1032,21 @@ class OpenPgpMessageInputStream(
} }
} }
companion object { private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) {
@JvmStatic val verifierProvider = api.implementation.pgpContentVerifierBuilderProvider()
private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) { try {
val verifierProvider = signature.init(verifierProvider, publicKey)
OpenPGPImplementation.getInstance().pgpContentVerifierBuilderProvider() } catch (e: PGPException) {
try { throw RuntimeException(e)
signature.init(verifierProvider, publicKey)
} catch (e: PGPException) {
throw RuntimeException(e)
}
} }
}
@JvmStatic private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) {
private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) { val verifierProvider = api.implementation.pgpContentVerifierBuilderProvider()
val verifierProvider = try {
OpenPGPImplementation.getInstance().pgpContentVerifierBuilderProvider() ops.init(verifierProvider, publicKey)
try { } catch (e: PGPException) {
ops.init(verifierProvider, publicKey) throw RuntimeException(e)
} catch (e: PGPException) {
throw RuntimeException(e)
}
} }
} }
} }
@ -1074,32 +1056,27 @@ class OpenPgpMessageInputStream(
private val LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream::class.java) private val LOGGER = LoggerFactory.getLogger(OpenPgpMessageInputStream::class.java)
@JvmStatic @JvmStatic
fun create(inputStream: InputStream, options: ConsumerOptions) = fun create(inputStream: InputStream, options: ConsumerOptions, api: PGPainless) =
create(inputStream, options, PGPainless.getInstance().algorithmPolicy) create(inputStream, options, Message(), api)
@JvmStatic
fun create(inputStream: InputStream, options: ConsumerOptions, policy: Policy) =
create(inputStream, options, Message(), policy)
@JvmStatic @JvmStatic
internal fun create( internal fun create(
inputStream: InputStream, inputStream: InputStream,
options: ConsumerOptions, options: ConsumerOptions,
metadata: Layer, metadata: Layer,
policy: Policy api: PGPainless
): OpenPgpMessageInputStream { ): OpenPgpMessageInputStream {
val openPgpIn = OpenPgpInputStream(inputStream) val openPgpIn = OpenPgpInputStream(inputStream)
openPgpIn.reset() openPgpIn.reset()
if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) { if (openPgpIn.isNonOpenPgp || options.isForceNonOpenPgpData()) {
return OpenPgpMessageInputStream( return OpenPgpMessageInputStream(
Type.non_openpgp, openPgpIn, options, metadata, policy) Type.non_openpgp, openPgpIn, options, metadata, api)
} }
if (openPgpIn.isBinaryOpenPgp) { if (openPgpIn.isBinaryOpenPgp) {
// Simply consume OpenPGP message // Simply consume OpenPGP message
return OpenPgpMessageInputStream( return OpenPgpMessageInputStream(Type.standard, openPgpIn, options, metadata, api)
Type.standard, openPgpIn, options, metadata, policy)
} }
return if (openPgpIn.isAsciiArmored) { return if (openPgpIn.isAsciiArmored) {
@ -1107,10 +1084,10 @@ class OpenPgpMessageInputStream(
if (armorIn.isClearText) { if (armorIn.isClearText) {
(metadata as Message).setCleartextSigned() (metadata as Message).setCleartextSigned()
OpenPgpMessageInputStream( OpenPgpMessageInputStream(
Type.cleartext_signed, armorIn, options, metadata, policy) Type.cleartext_signed, armorIn, options, metadata, api)
} else { } else {
// Simply consume dearmored OpenPGP message // Simply consume dearmored OpenPGP message
OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, policy) OpenPgpMessageInputStream(Type.standard, armorIn, options, metadata, api)
} }
} else { } else {
throw AssertionError("Cannot deduce type of data.") throw AssertionError("Cannot deduce type of data.")

View file

@ -8,7 +8,6 @@ import java.util.*
import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.api.OpenPGPCertificate 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.operator.PGPKeyEncryptionMethodGenerator import org.bouncycastle.openpgp.operator.PGPKeyEncryptionMethodGenerator
import org.pgpainless.PGPainless import org.pgpainless.PGPainless
import org.pgpainless.PGPainless.Companion.inspectKeyRing import org.pgpainless.PGPainless.Companion.inspectKeyRing
@ -334,9 +333,9 @@ class EncryptionOptions(
_encryptionKeys.add(key) _encryptionKeys.add(key)
_encryptionKeyIdentifiers.add(SubkeyIdentifier(key)) _encryptionKeyIdentifiers.add(SubkeyIdentifier(key))
addEncryptionMethod( addEncryptionMethod(
OpenPGPImplementation.getInstance() api.implementation.publicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey).also {
.publicKeyKeyEncryptionMethodGenerator(key.pgpPublicKey) it.setUseWildcardRecipient(wildcardRecipient)
.also { it.setUseWildcardRecipient(wildcardRecipient) }) })
} }
/** /**
@ -359,8 +358,7 @@ class EncryptionOptions(
fun addMessagePassphrase(passphrase: Passphrase) = apply { fun addMessagePassphrase(passphrase: Passphrase) = apply {
require(!passphrase.isEmpty) { "Passphrase MUST NOT be empty." } require(!passphrase.isEmpty) { "Passphrase MUST NOT be empty." }
addEncryptionMethod( addEncryptionMethod(
OpenPGPImplementation.getInstance() api.implementation.pbeKeyEncryptionMethodGenerator(passphrase.getChars()))
.pbeKeyEncryptionMethodGenerator(passphrase.getChars()))
} }
/** /**

View file

@ -697,7 +697,7 @@ public class OpenPgpMessageInputStreamTest {
throws IOException { throws IOException {
ByteArrayInputStream bytesIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8)); ByteArrayInputStream bytesIn = new ByteArrayInputStream(armored.getBytes(StandardCharsets.UTF_8));
ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn); ArmoredInputStream armorIn = ArmoredInputStreamFactory.get(bytesIn);
OpenPgpMessageInputStream pgpIn = OpenPgpMessageInputStream.create(armorIn, options); OpenPgpMessageInputStream pgpIn = OpenPgpMessageInputStream.create(armorIn, options, PGPainless.getInstance());
return pgpIn; return pgpIn;
} }
} }