1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2024-12-22 19:08:00 +01:00

Improve public/secret key selection

This commit is contained in:
Paul Schaub 2023-09-12 15:43:55 +02:00
parent 76cf6173e8
commit bb796143ff
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
3 changed files with 86 additions and 42 deletions

View file

@ -53,4 +53,10 @@ fun PGPSecretKeyRing.getSecretKeyFor(signature: PGPSignature): PGPSecretKey? =
* Return the [PGPSecretKey] that matches the key-ID of the given [PGPOnePassSignature] packet. * Return the [PGPSecretKey] that matches the key-ID of the given [PGPOnePassSignature] packet.
*/ */
fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGPSecretKey? = fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGPSecretKey? =
this.getSecretKey(onePassSignature.keyID) this.getSecretKey(onePassSignature.keyID)
fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? =
when(pkesk.version) {
3 -> this.getSecretKey(pkesk.keyID)
else -> throw NotImplementedError("Version 6 PKESKs are not yet supported.")
}

View file

@ -4,6 +4,7 @@
package org.pgpainless.decryption_verification package org.pgpainless.decryption_verification
import org.bouncycastle.extensions.getPublicKeyFor
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
@ -385,6 +386,11 @@ class ConsumerOptions {
fun getCertificate(keyId: Long): PGPPublicKeyRing? { fun getCertificate(keyId: Long): PGPPublicKeyRing? {
return explicitCertificates.firstOrNull { it.getPublicKey(keyId) != null } return explicitCertificates.firstOrNull { it.getPublicKey(keyId) != null }
} }
fun getCertificate(signature: PGPSignature): PGPPublicKeyRing? =
explicitCertificates.firstOrNull {
it.getPublicKeyFor(signature) != null
}
} }
companion object { companion object {

View file

@ -7,6 +7,8 @@ package org.pgpainless.decryption_verification
import openpgp.openPgpKeyId import openpgp.openPgpKeyId
import org.bouncycastle.bcpg.BCPGInputStream import org.bouncycastle.bcpg.BCPGInputStream
import org.bouncycastle.bcpg.UnsupportedPacketVersionException import org.bouncycastle.bcpg.UnsupportedPacketVersionException
import org.bouncycastle.extensions.getPublicKeyFor
import org.bouncycastle.extensions.getSecretKeyFor
import org.bouncycastle.extensions.unlock import org.bouncycastle.extensions.unlock
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
@ -280,31 +282,28 @@ class OpenPgpMessageInputStream(
val postponedDueToMissingPassphrase = mutableListOf<Pair<PGPSecretKey, PGPPublicKeyEncryptedData>>() val postponedDueToMissingPassphrase = mutableListOf<Pair<PGPSecretKey, PGPPublicKeyEncryptedData>>()
// try (known) secret keys // try (known) secret keys
for (pkesk in esks.pkesks) { esks.pkesks.forEach { pkesk ->
val keyId = pkesk.keyID LOGGER.debug("Encountered PKESK for recipient ${pkesk.keyID.openPgpKeyId()}")
LOGGER.debug("Encountered PKESK for recipient ${keyId.openPgpKeyId()}") val decryptionKeyCandidates = getDecryptionKeys(pkesk)
val decryptionKeys = getDecryptionKey(keyId) for (decryptionKeys in decryptionKeyCandidates) {
if (decryptionKeys == null) { val secretKey = decryptionKeys.getSecretKeyFor(pkesk)!!
LOGGER.debug("Skipping PKESK because no matching key ${keyId.openPgpKeyId()} was provided.") val decryptionKeyId = SubkeyIdentifier(decryptionKeys, secretKey.keyID)
continue if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
} continue
val secretKey = decryptionKeys.getSecretKey(keyId) }
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, keyId)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue
}
LOGGER.debug("Attempt decryption using secret key $decryptionKeyId") LOGGER.debug("Attempt decryption using secret key $decryptionKeyId")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
if (!protector.hasPassphraseFor(keyId)) { if (!protector.hasPassphraseFor(secretKey.keyID)) {
LOGGER.debug("Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") LOGGER.debug("Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.")
postponedDueToMissingPassphrase.add(secretKey to pkesk) postponedDueToMissingPassphrase.add(secretKey to pkesk)
continue continue
} }
val privateKey = secretKey.unlock(protector) val privateKey = secretKey.unlock(protector)
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) {
return true return true
}
} }
} }
@ -343,7 +342,7 @@ class OpenPgpMessageInputStream(
} else if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.INTERACTIVE) { } else if (options.getMissingKeyPassphraseStrategy() == MissingKeyPassphraseStrategy.INTERACTIVE) {
for ((secretKey, pkesk) in postponedDueToMissingPassphrase) { for ((secretKey, pkesk) in postponedDueToMissingPassphrase) {
val keyId = secretKey.keyID val keyId = secretKey.keyID
val decryptionKeys = getDecryptionKey(keyId)!! val decryptionKeys = getDecryptionKey(pkesk)!!
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, keyId) val decryptionKeyId = SubkeyIdentifier(decryptionKeys, keyId)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) { if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue continue
@ -541,6 +540,25 @@ class OpenPgpMessageInputStream(
}) })
} }
private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): PGPSecretKeyRing? = options.getDecryptionKeys().firstOrNull {
it.getSecretKeyFor(pkesk) != null && PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
when (pkesk.version) {
3 -> pkesk.keyID == subkey.keyID
else -> throw NotImplementedError("Version 6 PKESK not yet supported.")
}
}
}
private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<PGPSecretKeyRing> =
options.getDecryptionKeys().filter {
it.getSecretKeyFor(pkesk) != null && PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
when (pkesk.version) {
3 -> pkesk.keyID == subkey.keyID
else -> throw NotImplementedError("Version 6 PKESK not yet supported.")
}
}
}
private fun findPotentialDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<Pair<PGPSecretKeyRing, PGPSecretKey>> { private fun findPotentialDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<Pair<PGPSecretKeyRing, PGPSecretKey>> {
val algorithm = pkesk.algorithm val algorithm = pkesk.algorithm
val candidates = mutableListOf<Pair<PGPSecretKeyRing, PGPSecretKey>>() val candidates = mutableListOf<Pair<PGPSecretKeyRing, PGPSecretKey>>()
@ -638,22 +656,24 @@ class OpenPgpMessageInputStream(
} }
fun initializeSignature(signature: PGPSignature): SignatureCheck? { fun initializeSignature(signature: PGPSignature): SignatureCheck? {
val keyId = SignatureUtils.determineIssuerKeyId(signature) val certificate = findCertificate(signature) ?: return null
val certificate = findCertificate(keyId) ?: return null val publicKey = certificate.getPublicKeyFor(signature) ?: return null
val verifierKey = SubkeyIdentifier(certificate, publicKey.keyID)
val verifierKey = SubkeyIdentifier(certificate, keyId) initialize(signature, publicKey)
initialize(signature, certificate, keyId)
return SignatureCheck(signature, certificate, verifierKey) return SignatureCheck(signature, certificate, verifierKey)
} }
fun addOnePassSignature(signature: PGPOnePassSignature) { fun addOnePassSignature(signature: PGPOnePassSignature) {
val certificate = findCertificate(signature.keyID) val certificate = findCertificate(signature)
if (certificate != null) { if (certificate != null) {
val ops = OnePassSignatureCheck(signature, certificate) val publicKey = certificate.getPublicKeyFor(signature)
initialize(signature, certificate) if (publicKey != null) {
onePassSignatures.add(ops) val ops = OnePassSignatureCheck(signature, certificate)
literalOPS.add(ops) initialize(signature, publicKey)
onePassSignatures.add(ops)
literalOPS.add(ops)
}
} }
if (signature.isContaining) { if (signature.isContaining) {
enterNesting() enterNesting()
@ -710,14 +730,26 @@ class OpenPgpMessageInputStream(
opsUpdateStack.removeFirst() opsUpdateStack.removeFirst()
} }
fun findCertificate(keyId: Long): PGPPublicKeyRing? { private fun findCertificate(signature: PGPSignature): PGPPublicKeyRing? {
val cert = options.getCertificateSource().getCertificate(keyId) val cert = options.getCertificateSource().getCertificate(signature)
if (cert != null) { if (cert != null) {
return cert return cert
} }
if (options.getMissingCertificateCallback() != null) { if (options.getMissingCertificateCallback() != null) {
return options.getMissingCertificateCallback()!!.onMissingPublicKeyEncountered(keyId) return options.getMissingCertificateCallback()!!.onMissingPublicKeyEncountered(signature.keyID)
}
return null // TODO: Missing cert for sig
}
private fun findCertificate(signature: PGPOnePassSignature): PGPPublicKeyRing? {
val cert = options.getCertificateSource().getCertificate(signature.keyID)
if (cert != null) {
return cert
}
if (options.getMissingCertificateCallback() != null) {
return options.getMissingCertificateCallback()!!.onMissingPublicKeyEncountered(signature.keyID)
} }
return null // TODO: Missing cert for sig return null // TODO: Missing cert for sig
} }
@ -831,22 +863,22 @@ class OpenPgpMessageInputStream(
companion object { companion object {
@JvmStatic @JvmStatic
private fun initialize(signature: PGPSignature, certificate: PGPPublicKeyRing, keyId: Long) { private fun initialize(signature: PGPSignature, publicKey: PGPPublicKey) {
val verifierProvider = ImplementationFactory.getInstance() val verifierProvider = ImplementationFactory.getInstance()
.pgpContentVerifierBuilderProvider .pgpContentVerifierBuilderProvider
try { try {
signature.init(verifierProvider, certificate.getPublicKey(keyId)) signature.init(verifierProvider, publicKey)
} catch (e : PGPException) { } catch (e : PGPException) {
throw RuntimeException(e) throw RuntimeException(e)
} }
} }
@JvmStatic @JvmStatic
private fun initialize(ops: PGPOnePassSignature, certificate: PGPPublicKeyRing) { private fun initialize(ops: PGPOnePassSignature, publicKey: PGPPublicKey) {
val verifierProvider = ImplementationFactory.getInstance() val verifierProvider = ImplementationFactory.getInstance()
.pgpContentVerifierBuilderProvider .pgpContentVerifierBuilderProvider
try { try {
ops.init(verifierProvider, certificate.getPublicKey(ops.keyID)) ops.init(verifierProvider, publicKey)
} catch (e : PGPException) { } catch (e : PGPException) {
throw RuntimeException(e) throw RuntimeException(e)
} }