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:
parent
76cf6173e8
commit
bb796143ff
3 changed files with 86 additions and 42 deletions
|
@ -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.")
|
||||||
|
}
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue