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

WIP: Transform Options and OpenPgpMessageInputStream

This commit is contained in:
Paul Schaub 2025-02-10 17:05:16 +01:00
parent 53053cf3fc
commit 217a25bd62
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
13 changed files with 231 additions and 111 deletions

View file

@ -10,6 +10,7 @@ import org.bouncycastle.bcpg.SecretKeyPacket;
import org.bouncycastle.bcpg.SecretSubkeyPacket; import org.bouncycastle.bcpg.SecretSubkeyPacket;
import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.SubkeyIdentifier;
import javax.annotation.Nonnull; import javax.annotation.Nonnull;
@ -60,6 +61,11 @@ public final class GnuPGDummyKeyUtil {
return hardwareBackedKeys; return hardwareBackedKeys;
} }
public static Builder modify(@Nonnull OpenPGPKey key)
{
return modify(key.getPGPSecretKeyRing());
}
/** /**
* Modify the given {@link PGPSecretKeyRing}. * Modify the given {@link PGPSecretKeyRing}.
* *

View file

@ -11,7 +11,10 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPApi import org.bouncycastle.openpgp.api.OpenPGPApi
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPImplementation import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKeyReader
import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi import org.bouncycastle.openpgp.api.bc.BcOpenPGPApi
import org.pgpainless.algorithm.OpenPGPKeyVersion import org.pgpainless.algorithm.OpenPGPKeyVersion
import org.pgpainless.bouncycastle.PolicyAdapter import org.pgpainless.bouncycastle.PolicyAdapter
@ -43,6 +46,14 @@ class PGPainless(
fun generateKey(version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4): KeyRingTemplates = fun generateKey(version: OpenPGPKeyVersion = OpenPGPKeyVersion.v4): KeyRingTemplates =
KeyRingTemplates(version) KeyRingTemplates(version)
fun readKey(): OpenPGPKeyReader = api.readKeyOrCertificate()
fun toKey(secretKeyRing: PGPSecretKeyRing): OpenPGPKey =
OpenPGPKey(secretKeyRing, implementation)
fun toCertificate(publicKeyRing: PGPPublicKeyRing): OpenPGPCertificate =
OpenPGPCertificate(publicKeyRing, implementation)
companion object { companion object {
@Volatile private var instance: PGPainless? = null @Volatile private var instance: PGPainless? = null
@ -81,7 +92,9 @@ class PGPainless(
* *
* @return builder * @return builder
*/ */
@JvmStatic fun readKeyRing() = KeyRingReader() @Deprecated("Use readKey() instead.", replaceWith = ReplaceWith("readKey()"))
@JvmStatic
fun readKeyRing() = KeyRingReader()
/** /**
* Extract a public key certificate from a secret key. * Extract a public key certificate from a secret key.
@ -90,6 +103,7 @@ class PGPainless(
* @return public key certificate * @return public key certificate
*/ */
@JvmStatic @JvmStatic
@Deprecated("Use toKey() and then .toCertificate() instead.")
fun extractCertificate(secretKey: PGPSecretKeyRing) = fun extractCertificate(secretKey: PGPSecretKeyRing) =
KeyRingUtils.publicKeyRingFrom(secretKey) KeyRingUtils.publicKeyRingFrom(secretKey)
@ -190,6 +204,9 @@ class PGPainless(
fun inspectKeyRing(key: PGPKeyRing, referenceTime: Date = Date()) = fun inspectKeyRing(key: PGPKeyRing, referenceTime: Date = Date()) =
KeyRingInfo(key, referenceTime) KeyRingInfo(key, referenceTime)
fun inspectKeyRing(key: OpenPGPKey, referenceTime: Date = Date()) =
KeyRingInfo(key, getPolicy(), referenceTime)
/** /**
* Access, and make changes to PGPainless policy on acceptable/default algorithms etc. * Access, and make changes to PGPainless policy on acceptable/default algorithms etc.
* *

View file

@ -0,0 +1,8 @@
package org.pgpainless.bouncycastle.extensions
import org.bouncycastle.openpgp.PGPOnePassSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
fun OpenPGPCertificate.getSigningKeyFor(ops: PGPOnePassSignature): OpenPGPComponentKey? =
this.getKey(ops.keyIdentifier)

View file

@ -0,0 +1,8 @@
package org.pgpainless.bouncycastle.extensions
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
fun OpenPGPKey.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): OpenPGPSecretKey? =
this.getSecretKey(pkesk.keyIdentifier)

View file

@ -73,7 +73,4 @@ fun PGPSecretKeyRing.getSecretKeyFor(onePassSignature: PGPOnePassSignature): PGP
this.getSecretKey(onePassSignature.keyID) this.getSecretKey(onePassSignature.keyID)
fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? = fun PGPSecretKeyRing.getSecretKeyFor(pkesk: PGPPublicKeyEncryptedData): PGPSecretKey? =
when (pkesk.version) { this.getSecretKey(pkesk.keyIdentifier)
3 -> this.getSecretKey(pkesk.keyID)
else -> throw NotImplementedError("Version 6 PKESKs are not yet supported.")
}

View file

@ -4,11 +4,16 @@
package org.pgpainless.decryption_verification package org.pgpainless.decryption_verification
import org.bouncycastle.bcpg.KeyIdentifier
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.* import java.util.*
import org.bouncycastle.openpgp.* import org.bouncycastle.openpgp.*
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPImplementation
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.pgpainless.PGPainless
import org.pgpainless.bouncycastle.extensions.getPublicKeyFor import org.pgpainless.bouncycastle.extensions.getPublicKeyFor
import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy import org.pgpainless.decryption_verification.cleartext_signatures.InMemoryMultiPassStrategy
import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy import org.pgpainless.decryption_verification.cleartext_signatures.MultiPassStrategy
@ -19,7 +24,9 @@ import org.pgpainless.util.Passphrase
import org.pgpainless.util.SessionKey import org.pgpainless.util.SessionKey
/** Options for decryption and signature verification. */ /** Options for decryption and signature verification. */
class ConsumerOptions { class ConsumerOptions(
private val implementation: OpenPGPImplementation
) {
private var ignoreMDCErrors = false private var ignoreMDCErrors = false
var isDisableAsciiArmorCRC = false var isDisableAsciiArmorCRC = false
@ -34,7 +41,7 @@ class ConsumerOptions {
private var sessionKey: SessionKey? = null private var sessionKey: SessionKey? = null
private val customDecryptorFactories = private val customDecryptorFactories =
mutableMapOf<SubkeyIdentifier, PublicKeyDataDecryptorFactory>() mutableMapOf<SubkeyIdentifier, PublicKeyDataDecryptorFactory>()
private val decryptionKeys = mutableMapOf<PGPSecretKeyRing, SecretKeyRingProtector>() private val decryptionKeys = mutableMapOf<OpenPGPKey, SecretKeyRingProtector>()
private val decryptionPassphrases = mutableSetOf<Passphrase>() private val decryptionPassphrases = mutableSetOf<Passphrase>()
private var missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE private var missingKeyPassphraseStrategy = MissingKeyPassphraseStrategy.INTERACTIVE
private var multiPassStrategy: MultiPassStrategy = InMemoryMultiPassStrategy() private var multiPassStrategy: MultiPassStrategy = InMemoryMultiPassStrategy()
@ -65,6 +72,10 @@ class ConsumerOptions {
fun getVerifyNotAfter() = verifyNotAfter fun getVerifyNotAfter() = verifyNotAfter
fun addVerificationCert(verificationCert: OpenPGPCertificate): ConsumerOptions = apply {
this.certificates.addCertificate(verificationCert)
}
/** /**
* Add a certificate (public key ring) for signature verification. * Add a certificate (public key ring) for signature verification.
* *
@ -155,6 +166,12 @@ class ConsumerOptions {
fun getSessionKey() = sessionKey fun getSessionKey() = sessionKey
@JvmOverloads
fun addDecryptionKey(
key: OpenPGPKey,
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()
) = apply { decryptionKeys[key] = protector }
/** /**
* Add a key for message decryption. If the key is encrypted, the [SecretKeyRingProtector] is * Add a key for message decryption. If the key is encrypted, the [SecretKeyRingProtector] is
* used to decrypt it when needed. * used to decrypt it when needed.
@ -167,7 +184,7 @@ class ConsumerOptions {
fun addDecryptionKey( fun addDecryptionKey(
key: PGPSecretKeyRing, key: PGPSecretKeyRing,
protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys() protector: SecretKeyRingProtector = SecretKeyRingProtector.unprotectedKeys()
) = apply { decryptionKeys[key] = protector } ) = addDecryptionKey(OpenPGPKey(key, implementation), protector)
/** /**
* Add the keys in the provided key collection for message decryption. * Add the keys in the provided key collection for message decryption.
@ -270,7 +287,7 @@ class ConsumerOptions {
* @param decryptionKeyRing secret key * @param decryptionKeyRing secret key
* @return protector for that particular secret key * @return protector for that particular secret key
*/ */
fun getSecretKeyProtector(decryptionKeyRing: PGPSecretKeyRing): SecretKeyRingProtector? { fun getSecretKeyProtector(decryptionKeyRing: OpenPGPKey): SecretKeyRingProtector? {
return decryptionKeys[decryptionKeyRing] return decryptionKeys[decryptionKeyRing]
} }
@ -378,14 +395,20 @@ class ConsumerOptions {
* available signer certificates. * available signer certificates.
*/ */
class CertificateSource { class CertificateSource {
private val explicitCertificates: MutableSet<PGPPublicKeyRing> = mutableSetOf() private val explicitCertificates: MutableSet<OpenPGPCertificate> = mutableSetOf()
/** /**
* Add a certificate as verification cert explicitly. * Add a certificate as verification cert explicitly.
* *
* @param certificate certificate * @param certificate certificate
*/ */
fun addCertificate(certificate: PGPPublicKeyRing) { fun addCertificate(certificate: PGPPublicKeyRing,
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
) {
explicitCertificates.add(OpenPGPCertificate(certificate, implementation))
}
fun addCertificate(certificate: OpenPGPCertificate) {
explicitCertificates.add(certificate) explicitCertificates.add(certificate)
} }
@ -394,7 +417,7 @@ class ConsumerOptions {
* *
* @return explicitly set verification certs * @return explicitly set verification certs
*/ */
fun getExplicitCertificates(): Set<PGPPublicKeyRing> { fun getExplicitCertificates(): Set<OpenPGPCertificate> {
return explicitCertificates.toSet() return explicitCertificates.toSet()
} }
@ -406,15 +429,23 @@ class ConsumerOptions {
* @param keyId key id * @param keyId key id
* @return certificate * @return certificate
*/ */
fun getCertificate(keyId: Long): PGPPublicKeyRing? { fun getCertificate(keyId: Long): OpenPGPCertificate? {
return explicitCertificates.firstOrNull { it.getPublicKey(keyId) != null } return getCertificate(KeyIdentifier(keyId))
} }
fun getCertificate(signature: PGPSignature): PGPPublicKeyRing? = fun getCertificate(identifier: KeyIdentifier): OpenPGPCertificate? {
explicitCertificates.firstOrNull { it.getPublicKeyFor(signature) != null } return explicitCertificates.firstOrNull { it.getKey(identifier) != null }
}
fun getCertificate(signature: PGPSignature): OpenPGPCertificate? =
explicitCertificates.firstOrNull { it.getSigningKeyFor(signature) != null }
} }
companion object { companion object {
@JvmStatic fun get() = ConsumerOptions() @JvmStatic
@JvmOverloads
fun get(
implementation: OpenPGPImplementation = PGPainless.getInstance().implementation
) = ConsumerOptions(implementation)
} }
} }

View file

@ -4,10 +4,12 @@
package org.pgpainless.decryption_verification package org.pgpainless.decryption_verification
import org.bouncycastle.bcpg.KeyIdentifier
import java.util.* import java.util.*
import javax.annotation.Nonnull import javax.annotation.Nonnull
import org.bouncycastle.openpgp.PGPKeyRing import org.bouncycastle.openpgp.PGPKeyRing
import org.bouncycastle.openpgp.PGPLiteralData import org.bouncycastle.openpgp.PGPLiteralData
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.pgpainless.algorithm.CompressionAlgorithm import org.pgpainless.algorithm.CompressionAlgorithm
import org.pgpainless.algorithm.StreamEncoding import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
@ -47,9 +49,15 @@ class MessageMetadata(val message: Message) {
if (encryptionAlgorithm == null) false if (encryptionAlgorithm == null) false
else encryptionAlgorithm != SymmetricKeyAlgorithm.NULL else encryptionAlgorithm != SymmetricKeyAlgorithm.NULL
fun isEncryptedFor(keys: PGPKeyRing): Boolean { fun isEncryptedFor(cert: OpenPGPCertificate): Boolean {
return encryptionLayers.asSequence().any { return encryptionLayers.asSequence().any {
it.recipients.any { keyId -> keys.getPublicKey(keyId) != null } it.recipients.any { keyId -> cert.getKey(KeyIdentifier(keyId)) != null }
}
}
fun isEncryptedFor(cert: PGPKeyRing): Boolean {
return encryptionLayers.asSequence().any {
it.recipients.any { keyId -> cert.getPublicKey(keyId) != null }
} }
} }
@ -270,6 +278,9 @@ class MessageMetadata(val message: Message) {
fun isVerifiedSignedBy(keys: PGPKeyRing) = fun isVerifiedSignedBy(keys: PGPKeyRing) =
verifiedSignatures.any { keys.matches(it.signingKey) } verifiedSignatures.any { keys.matches(it.signingKey) }
fun isVerifiedSignedBy(cert: OpenPGPCertificate) =
verifiedSignatures.any { cert.pgpKeyRing.matches(it.signingKey) }
fun isVerifiedDetachedSignedBy(fingerprint: OpenPgpFingerprint) = fun isVerifiedDetachedSignedBy(fingerprint: OpenPgpFingerprint) =
verifiedDetachedSignatures.any { it.signingKey.matches(fingerprint) } verifiedDetachedSignatures.any { it.signingKey.matches(fingerprint) }

View file

@ -18,6 +18,7 @@ import org.bouncycastle.openpgp.PGPCompressedData
import org.bouncycastle.openpgp.PGPEncryptedData import org.bouncycastle.openpgp.PGPEncryptedData
import org.bouncycastle.openpgp.PGPEncryptedDataList import org.bouncycastle.openpgp.PGPEncryptedDataList
import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPKeyPair
import org.bouncycastle.openpgp.PGPOnePassSignature import org.bouncycastle.openpgp.PGPOnePassSignature
import org.bouncycastle.openpgp.PGPPBEEncryptedData import org.bouncycastle.openpgp.PGPPBEEncryptedData
import org.bouncycastle.openpgp.PGPPrivateKey import org.bouncycastle.openpgp.PGPPrivateKey
@ -27,6 +28,10 @@ import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.api.OpenPGPKey.OpenPGPSecretKey
import org.bouncycastle.openpgp.api.OpenPGPSignature.OpenPGPDocumentSignature
import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory import org.bouncycastle.openpgp.operator.PBEDataDecryptorFactory
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory
import org.bouncycastle.util.io.TeeInputStream import org.bouncycastle.util.io.TeeInputStream
@ -37,6 +42,7 @@ import org.pgpainless.algorithm.StreamEncoding
import org.pgpainless.algorithm.SymmetricKeyAlgorithm import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.bouncycastle.extensions.getPublicKeyFor import org.pgpainless.bouncycastle.extensions.getPublicKeyFor
import org.pgpainless.bouncycastle.extensions.getSecretKeyFor import org.pgpainless.bouncycastle.extensions.getSecretKeyFor
import org.pgpainless.bouncycastle.extensions.getSigningKeyFor
import org.pgpainless.bouncycastle.extensions.issuerKeyId import org.pgpainless.bouncycastle.extensions.issuerKeyId
import org.pgpainless.bouncycastle.extensions.unlock import org.pgpainless.bouncycastle.extensions.unlock
import org.pgpainless.decryption_verification.MessageMetadata.CompressedData import org.pgpainless.decryption_verification.MessageMetadata.CompressedData
@ -66,6 +72,7 @@ import org.pgpainless.signature.consumer.SignatureValidator
import org.pgpainless.util.ArmoredInputStreamFactory import org.pgpainless.util.ArmoredInputStreamFactory
import org.pgpainless.util.SessionKey import org.pgpainless.util.SessionKey
import org.slf4j.LoggerFactory import org.slf4j.LoggerFactory
import kotlin.math.sign
class OpenPgpMessageInputStream( class OpenPgpMessageInputStream(
type: Type, type: Type,
@ -400,30 +407,33 @@ class OpenPgpMessageInputStream(
} }
val postponedDueToMissingPassphrase = val postponedDueToMissingPassphrase =
mutableListOf<Pair<PGPSecretKey, PGPPublicKeyEncryptedData>>() mutableListOf<Pair<OpenPGPSecretKey, PGPPublicKeyEncryptedData>>()
// try (known) secret keys // try (known) secret keys
esks.pkesks.forEach { pkesk -> esks.pkesks.forEach { pkesk ->
LOGGER.debug("Encountered PKESK for recipient ${pkesk.keyID.openPgpKeyId()}") LOGGER.debug("Encountered PKESK for recipient ${pkesk.keyIdentifier}")
val decryptionKeyCandidates = getDecryptionKeys(pkesk) val decryptionKeyCandidates = getDecryptionKeys(pkesk)
for (decryptionKeys in decryptionKeyCandidates) { for (decryptionKeys in decryptionKeyCandidates) {
val secretKey = decryptionKeys.getSecretKeyFor(pkesk)!! val secretKey = decryptionKeys.getSecretKeyFor(pkesk)!!
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, secretKey.keyID) if (hasUnsupportedS2KSpecifier(secretKey)) {
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue continue
} }
LOGGER.debug("Attempt decryption using secret key $decryptionKeyId") LOGGER.debug("Attempt decryption using secret key ${decryptionKeys.keyIdentifier}")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue
if (!protector.hasPassphraseFor(secretKey.keyID)) { if (!protector.hasPassphraseFor(secretKey.keyIdentifier)) {
LOGGER.debug( LOGGER.debug(
"Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") "Missing passphrase for key ${decryptionKeys.keyIdentifier}. 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,
SubkeyIdentifier(secretKey.openPGPKey.pgpSecretKeyRing, secretKey.keyIdentifier),
pkesk)) {
return true return true
} }
} }
@ -431,24 +441,27 @@ class OpenPgpMessageInputStream(
// try anonymous secret keys // try anonymous secret keys
for (pkesk in esks.anonPkesks) { for (pkesk in esks.anonPkesks) {
for ((decryptionKeys, secretKey) in findPotentialDecryptionKeys(pkesk)) { for (decryptionKeys in findPotentialDecryptionKeys(pkesk)) {
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, secretKey.keyID) if (hasUnsupportedS2KSpecifier(decryptionKeys)) {
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) {
continue continue
} }
LOGGER.debug("Attempt decryption of anonymous PKESK with key $decryptionKeyId.") LOGGER.debug("Attempt decryption of anonymous PKESK with key $decryptionKeys.")
val protector = options.getSecretKeyProtector(decryptionKeys) ?: continue val protector = options.getSecretKeyProtector(decryptionKeys.openPGPKey) ?: continue
if (!protector.hasPassphraseFor(secretKey.keyID)) { if (!protector.hasPassphraseFor(decryptionKeys.keyIdentifier)) {
LOGGER.debug( LOGGER.debug(
"Missing passphrase for key $decryptionKeyId. Postponing decryption until all other keys were tried.") "Missing passphrase for key ${decryptionKeys.keyIdentifier}. Postponing decryption until all other keys were tried.")
postponedDueToMissingPassphrase.add(secretKey to pkesk) postponedDueToMissingPassphrase.add(decryptionKeys to pkesk)
continue continue
} }
val privateKey = secretKey.unlock(protector) val privateKey = decryptionKeys.unlock(protector)
if (decryptWithPrivateKey(esks, privateKey, decryptionKeyId, pkesk)) { if (decryptWithPrivateKey(
esks,
privateKey,
SubkeyIdentifier(decryptionKeys.openPGPKey.pgpSecretKeyRing, privateKey.keyIdentifier),
pkesk)) {
return true return true
} }
} }
@ -463,10 +476,10 @@ class OpenPgpMessageInputStream(
} else if (options.getMissingKeyPassphraseStrategy() == } else if (options.getMissingKeyPassphraseStrategy() ==
MissingKeyPassphraseStrategy.INTERACTIVE) { MissingKeyPassphraseStrategy.INTERACTIVE) {
for ((secretKey, pkesk) in postponedDueToMissingPassphrase) { for ((secretKey, pkesk) in postponedDueToMissingPassphrase) {
val keyId = secretKey.keyID val keyId = secretKey.keyIdentifier
val decryptionKeys = getDecryptionKey(pkesk)!! val decryptionKeys = getDecryptionKey(pkesk)!!
val decryptionKeyId = SubkeyIdentifier(decryptionKeys, keyId) val decryptionKeyId = SubkeyIdentifier(decryptionKeys.pgpSecretKeyRing, keyId)
if (hasUnsupportedS2KSpecifier(secretKey, decryptionKeyId)) { if (hasUnsupportedS2KSpecifier(secretKey)) {
continue continue
} }
@ -489,24 +502,23 @@ class OpenPgpMessageInputStream(
private fun decryptWithPrivateKey( private fun decryptWithPrivateKey(
esks: SortedESKs, esks: SortedESKs,
privateKey: PGPPrivateKey, privateKey: PGPKeyPair,
decryptionKeyId: SubkeyIdentifier, decryptionKeyId: SubkeyIdentifier,
pkesk: PGPPublicKeyEncryptedData pkesk: PGPPublicKeyEncryptedData
): Boolean { ): Boolean {
val decryptorFactory = val decryptorFactory =
ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey) ImplementationFactory.getInstance().getPublicKeyDataDecryptorFactory(privateKey.privateKey)
return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk) return decryptPKESKAndStream(esks, decryptionKeyId, decryptorFactory, pkesk)
} }
private fun hasUnsupportedS2KSpecifier( private fun hasUnsupportedS2KSpecifier(
secretKey: PGPSecretKey, secretKey: OpenPGPSecretKey
decryptionKeyId: SubkeyIdentifier
): Boolean { ): Boolean {
val s2k = secretKey.s2K val s2k = secretKey.pgpSecretKey.s2K
if (s2k != null) { if (s2k != null) {
if (s2k.type in 100..110) { if (s2k.type in 100..110) {
LOGGER.debug( LOGGER.debug(
"Skipping PKESK because key $decryptionKeyId has unsupported private S2K specifier ${s2k.type}") "Skipping PKESK because key ${secretKey.keyIdentifier} has unsupported private S2K specifier ${s2k.type}")
return true return true
} }
} }
@ -672,26 +684,26 @@ class OpenPgpMessageInputStream(
return MessageMetadata((layerMetadata as Message)) return MessageMetadata((layerMetadata as Message))
} }
private fun getDecryptionKey(keyId: Long): PGPSecretKeyRing? = private fun getDecryptionKey(keyId: Long): OpenPGPKey? =
options.getDecryptionKeys().firstOrNull { options.getDecryptionKeys().firstOrNull {
it.any { k -> k.keyID == keyId } it.pgpSecretKeyRing.any { k -> k.keyID == keyId }
.and( .and(
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k -> PGPainless.inspectKeyRing(it).decryptionSubkeys.any { k ->
k.keyIdentifier.keyId == keyId k.keyIdentifier.keyId == keyId
}) })
} }
private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): PGPSecretKeyRing? = private fun getDecryptionKey(pkesk: PGPPublicKeyEncryptedData): OpenPGPKey? =
options.getDecryptionKeys().firstOrNull { options.getDecryptionKeys().firstOrNull {
it.getSecretKeyFor(pkesk) != null && it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
pkesk.keyIdentifier.matches(subkey.keyIdentifier) pkesk.keyIdentifier.matches(subkey.keyIdentifier)
} }
} }
private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<PGPSecretKeyRing> = private fun getDecryptionKeys(pkesk: PGPPublicKeyEncryptedData): List<OpenPGPKey> =
options.getDecryptionKeys().filter { options.getDecryptionKeys().filter {
it.getSecretKeyFor(pkesk) != null && it.pgpSecretKeyRing.getSecretKeyFor(pkesk) != null &&
PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey -> PGPainless.inspectKeyRing(it).decryptionSubkeys.any { subkey ->
pkesk.keyIdentifier.matches(subkey.keyIdentifier) pkesk.keyIdentifier.matches(subkey.keyIdentifier)
} }
@ -699,15 +711,15 @@ class OpenPgpMessageInputStream(
private fun findPotentialDecryptionKeys( private fun findPotentialDecryptionKeys(
pkesk: PGPPublicKeyEncryptedData pkesk: PGPPublicKeyEncryptedData
): List<Pair<PGPSecretKeyRing, PGPSecretKey>> { ): List<OpenPGPSecretKey> {
val algorithm = pkesk.algorithm val algorithm = pkesk.algorithm
val candidates = mutableListOf<Pair<PGPSecretKeyRing, PGPSecretKey>>() val candidates = mutableListOf<OpenPGPSecretKey>()
options.getDecryptionKeys().forEach { options.getDecryptionKeys().forEach {
val info = PGPainless.inspectKeyRing(it) val info = PGPainless.inspectKeyRing(it)
for (key in info.decryptionSubkeys) { for (key in info.decryptionSubkeys) {
if (key.pgpPublicKey.algorithm == algorithm && if (key.pgpPublicKey.algorithm == algorithm &&
info.isSecretKeyAvailable(key.keyIdentifier)) { info.isSecretKeyAvailable(key.keyIdentifier)) {
candidates.add(it to it.getSecretKey(key.keyIdentifier)) candidates.add(it.getSecretKey(key.keyIdentifier))
} }
} }
} }
@ -753,8 +765,8 @@ class OpenPgpMessageInputStream(
} }
private class Signatures(val options: ConsumerOptions) : OutputStream() { private class Signatures(val options: ConsumerOptions) : OutputStream() {
val detachedSignatures = mutableListOf<SignatureCheck>() val detachedSignatures = mutableListOf<OpenPGPDocumentSignature>()
val prependedSignatures = mutableListOf<SignatureCheck>() val prependedSignatures = mutableListOf<OpenPGPDocumentSignature>()
val onePassSignatures = mutableListOf<OnePassSignatureCheck>() val onePassSignatures = mutableListOf<OnePassSignatureCheck>()
val opsUpdateStack = ArrayDeque<MutableList<OnePassSignatureCheck>>() val opsUpdateStack = ArrayDeque<MutableList<OnePassSignatureCheck>>()
var literalOPS = mutableListOf<OnePassSignatureCheck>() var literalOPS = mutableListOf<OnePassSignatureCheck>()
@ -798,22 +810,21 @@ class OpenPgpMessageInputStream(
} }
} }
fun initializeSignature(signature: PGPSignature): SignatureCheck? { fun initializeSignature(signature: PGPSignature): OpenPGPDocumentSignature? {
val certificate = findCertificate(signature) ?: return null val certificate = findCertificate(signature) ?: return null
val publicKey = certificate.getPublicKeyFor(signature) ?: return null val publicKey = certificate.getSigningKeyFor(signature) ?: return null
val verifierKey = SubkeyIdentifier(certificate, publicKey.keyID) initialize(signature, publicKey.pgpPublicKey)
initialize(signature, publicKey) return OpenPGPDocumentSignature(signature, publicKey)
return SignatureCheck(signature, certificate, verifierKey)
} }
fun addOnePassSignature(signature: PGPOnePassSignature) { fun addOnePassSignature(signature: PGPOnePassSignature) {
val certificate = findCertificate(signature) val certificate = findCertificate(signature)
if (certificate != null) { if (certificate != null) {
val publicKey = certificate.getPublicKeyFor(signature) val publicKey = certificate.getSigningKeyFor(signature)
if (publicKey != null) { if (publicKey != null) {
val ops = OnePassSignatureCheck(signature, certificate) val ops = OnePassSignatureCheck(signature, certificate)
initialize(signature, publicKey) initialize(signature, publicKey.pgpPublicKey)
onePassSignatures.add(ops) onePassSignatures.add(ops)
literalOPS.add(ops) literalOPS.add(ops)
} }
@ -844,7 +855,7 @@ class OpenPgpMessageInputStream(
val verification = val verification =
SignatureVerification( SignatureVerification(
signature, signature,
SubkeyIdentifier(check.verificationKeys, check.onePassSignature.keyID)) SubkeyIdentifier(check.verificationKeys.pgpPublicKeyRing, check.onePassSignature.keyIdentifier))
try { try {
SignatureValidator.signatureWasCreatedInBounds( SignatureValidator.signatureWasCreatedInBounds(
@ -882,7 +893,7 @@ class OpenPgpMessageInputStream(
opsUpdateStack.removeFirst() opsUpdateStack.removeFirst()
} }
private fun findCertificate(signature: PGPSignature): PGPPublicKeyRing? { private fun findCertificate(signature: PGPSignature): OpenPGPCertificate? {
val cert = options.getCertificateSource().getCertificate(signature) val cert = options.getCertificateSource().getCertificate(signature)
if (cert != null) { if (cert != null) {
return cert return cert
@ -896,7 +907,7 @@ class OpenPgpMessageInputStream(
return null // TODO: Missing cert for sig return null // TODO: Missing cert for sig
} }
private fun findCertificate(signature: PGPOnePassSignature): PGPPublicKeyRing? { private fun findCertificate(signature: PGPOnePassSignature): OpenPGPCertificate? {
val cert = options.getCertificateSource().getCertificate(signature.keyID) val cert = options.getCertificateSource().getCertificate(signature.keyID)
if (cert != null) { if (cert != null) {
return cert return cert
@ -977,7 +988,7 @@ class OpenPgpMessageInputStream(
for (prepended in prependedSignatures) { for (prepended in prependedSignatures) {
val verification = val verification =
SignatureVerification(prepended.signature, prepended.signingKeyIdentifier) SignatureVerification(prepended.signature, prepended.keyIdentifier)
try { try {
SignatureValidator.signatureWasCreatedInBounds( SignatureValidator.signatureWasCreatedInBounds(
options.getVerifyNotBefore(), options.getVerifyNotAfter()) options.getVerifyNotBefore(), options.getVerifyNotAfter())

View file

@ -4,15 +4,17 @@
package org.pgpainless.key.protection package org.pgpainless.key.protection
import kotlin.jvm.Throws import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.PGPException import org.bouncycastle.openpgp.PGPException
import org.bouncycastle.openpgp.PGPSecretKey import org.bouncycastle.openpgp.PGPSecretKey
import org.bouncycastle.openpgp.PGPSecretKeyRing import org.bouncycastle.openpgp.PGPSecretKeyRing
import org.bouncycastle.openpgp.api.KeyPassphraseProvider
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider import org.pgpainless.key.protection.passphrase_provider.SecretKeyPassphraseProvider
import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider import org.pgpainless.key.protection.passphrase_provider.SolitaryPassphraseProvider
import org.pgpainless.util.Passphrase import org.pgpainless.util.Passphrase
import kotlin.Throws
/** /**
* Task of the [SecretKeyRingProtector] is to map encryptor/decryptor objects to key-ids. * Task of the [SecretKeyRingProtector] is to map encryptor/decryptor objects to key-ids.
@ -22,7 +24,7 @@ import org.pgpainless.util.Passphrase
* While it is easy to create an implementation of this interface that fits your needs, there are a * While it is easy to create an implementation of this interface that fits your needs, there are a
* bunch of implementations ready for use. * bunch of implementations ready for use.
*/ */
interface SecretKeyRingProtector { interface SecretKeyRingProtector : KeyPassphraseProvider {
/** /**
* Returns true, if the protector has a passphrase for the key with the given key-id. * Returns true, if the protector has a passphrase for the key with the given key-id.
@ -30,7 +32,9 @@ interface SecretKeyRingProtector {
* @param keyId key id * @param keyId key id
* @return true if it has a passphrase, false otherwise * @return true if it has a passphrase, false otherwise
*/ */
fun hasPassphraseFor(keyId: Long): Boolean fun hasPassphraseFor(keyId: Long): Boolean = hasPassphraseFor(KeyIdentifier(keyId))
fun hasPassphraseFor(keyIdentifier: KeyIdentifier): Boolean
/** /**
* Return a decryptor for the key of id `keyId`. This method returns null if the key is * Return a decryptor for the key of id `keyId`. This method returns null if the key is
@ -39,7 +43,10 @@ interface SecretKeyRingProtector {
* @param keyId id of the key * @param keyId id of the key
* @return decryptor for the key * @return decryptor for the key
*/ */
@Throws(PGPException::class) fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? @Throws(PGPException::class) fun getDecryptor(keyId: Long): PBESecretKeyDecryptor? =
getDecryptor(KeyIdentifier(keyId))
@Throws(PGPException::class) fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor?
/** /**
* Return an encryptor for the key of id `keyId`. This method returns null if the key is * Return an encryptor for the key of id `keyId`. This method returns null if the key is
@ -48,7 +55,10 @@ interface SecretKeyRingProtector {
* @param keyId id of the key * @param keyId id of the key
* @return encryptor for the key * @return encryptor for the key
*/ */
@Throws(PGPException::class) fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? @Throws(PGPException::class) fun getEncryptor(keyId: Long): PBESecretKeyEncryptor? =
getEncryptor(KeyIdentifier(keyId))
@Throws(PGPException::class) fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor?
companion object { companion object {

View file

@ -3,14 +3,21 @@
// SPDX-License-Identifier: Apache-2.0 // SPDX-License-Identifier: Apache-2.0
package org.pgpainless.key.protection package org.pgpainless.key.protection
import org.bouncycastle.bcpg.KeyIdentifier
import org.bouncycastle.openpgp.api.OpenPGPKey
import org.bouncycastle.openpgp.operator.PBESecretKeyDecryptor
import org.bouncycastle.openpgp.operator.PBESecretKeyEncryptor
/** /**
* Implementation of the [SecretKeyRingProtector] which assumes that all handled keys are not * Implementation of the [SecretKeyRingProtector] which assumes that all handled keys are not
* password protected. * password protected.
*/ */
class UnprotectedKeysProtector : SecretKeyRingProtector { class UnprotectedKeysProtector : SecretKeyRingProtector {
override fun hasPassphraseFor(keyId: Long) = true override fun hasPassphraseFor(keyIdentifier: KeyIdentifier): Boolean = true
override fun getDecryptor(keyId: Long) = null override fun getDecryptor(keyIdentifier: KeyIdentifier): PBESecretKeyDecryptor? = null
override fun getEncryptor(keyId: Long) = null override fun getEncryptor(keyIdentifier: KeyIdentifier): PBESecretKeyEncryptor? = null
override fun getKeyPassword(p0: OpenPGPKey.OpenPGPSecretKey?): CharArray? = null
} }

View file

@ -7,6 +7,9 @@ package org.pgpainless.signature.consumer
import org.bouncycastle.openpgp.PGPOnePassSignature import org.bouncycastle.openpgp.PGPOnePassSignature
import org.bouncycastle.openpgp.PGPPublicKeyRing import org.bouncycastle.openpgp.PGPPublicKeyRing
import org.bouncycastle.openpgp.PGPSignature import org.bouncycastle.openpgp.PGPSignature
import org.bouncycastle.openpgp.api.OpenPGPCertificate
import org.bouncycastle.openpgp.api.OpenPGPCertificate.OpenPGPComponentKey
import org.pgpainless.bouncycastle.extensions.getSigningKeyFor
import org.pgpainless.key.SubkeyIdentifier import org.pgpainless.key.SubkeyIdentifier
/** /**
@ -20,7 +23,7 @@ import org.pgpainless.key.SubkeyIdentifier
*/ */
data class OnePassSignatureCheck( data class OnePassSignatureCheck(
val onePassSignature: PGPOnePassSignature, val onePassSignature: PGPOnePassSignature,
val verificationKeys: PGPPublicKeyRing, val verificationKeys: OpenPGPCertificate,
var signature: PGPSignature? = null var signature: PGPSignature? = null
) { ) {
@ -30,5 +33,5 @@ data class OnePassSignatureCheck(
* @return signing key fingerprint * @return signing key fingerprint
*/ */
val signingKey: SubkeyIdentifier val signingKey: SubkeyIdentifier
get() = SubkeyIdentifier(verificationKeys, onePassSignature.keyID) get() = SubkeyIdentifier(verificationKeys.pgpPublicKeyRing, onePassSignature.keyID)
} }

View file

@ -17,6 +17,8 @@ import org.bouncycastle.bcpg.S2K;
import org.bouncycastle.bcpg.SecretKeyPacket; import org.bouncycastle.bcpg.SecretKeyPacket;
import org.bouncycastle.openpgp.PGPSecretKey; import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
import org.pgpainless.PGPainless; import org.pgpainless.PGPainless;
import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.SubkeyIdentifier;
@ -178,8 +180,9 @@ public class GnuPGDummyKeyUtilTest {
@Test @Test
public void testMoveAllKeysToCard() throws IOException { public void testMoveAllKeysToCard() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
PGPSecretKeyRing expected = PGPainless.readKeyRing().secretKeyRing(ALL_KEYS_ON_CARD); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
OpenPGPKey expected = reader.parseKey(ALL_KEYS_ON_CARD);
PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.any(), cardSerial); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.any(), cardSerial);
@ -190,46 +193,50 @@ public class GnuPGDummyKeyUtilTest {
assertEquals(S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, s2K.getProtectionMode()); assertEquals(S2K.GNU_PROTECTION_MODE_DIVERT_TO_CARD, s2K.getProtectionMode());
} }
assertArrayEquals(expected.getEncoded(), onCard.getEncoded()); assertArrayEquals(expected.getPGPSecretKeyRing().getEncoded(), onCard.getEncoded());
} }
@Test @Test
public void testMovePrimaryKeyToCard() throws IOException { public void testMovePrimaryKeyToCard() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
PGPSecretKeyRing expected = PGPainless.readKeyRing().secretKeyRing(PRIMARY_KEY_ON_CARD); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
OpenPGPKey expected = reader.parseKey(PRIMARY_KEY_ON_CARD);
PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(primaryKeyId), cardSerial); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(primaryKeyId), cardSerial);
assertArrayEquals(expected.getEncoded(), onCard.getEncoded()); assertArrayEquals(expected.getPGPSecretKeyRing().getEncoded(), onCard.getEncoded());
} }
@Test @Test
public void testMoveEncryptionKeyToCard() throws IOException { public void testMoveEncryptionKeyToCard() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
PGPSecretKeyRing expected = PGPainless.readKeyRing().secretKeyRing(ENCRYPTION_KEY_ON_CARD); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
OpenPGPKey expected = reader.parseKey(ENCRYPTION_KEY_ON_CARD);
PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(encryptionKeyId), cardSerial); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(encryptionKeyId), cardSerial);
assertArrayEquals(expected.getEncoded(), onCard.getEncoded()); assertArrayEquals(expected.getPGPSecretKeyRing().getEncoded(), onCard.getEncoded());
} }
@Test @Test
public void testMoveSigningKeyToCard() throws IOException { public void testMoveSigningKeyToCard() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
PGPSecretKeyRing expected = PGPainless.readKeyRing().secretKeyRing(SIGNATURE_KEY_ON_CARD); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
OpenPGPKey expected = reader.parseKey(SIGNATURE_KEY_ON_CARD);
PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing onCard = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(signatureKeyId), cardSerial); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(signatureKeyId), cardSerial);
assertArrayEquals(expected.getEncoded(), onCard.getEncoded()); assertArrayEquals(expected.getPGPSecretKeyRing().getEncoded(), onCard.getEncoded());
} }
@Test @Test
public void testRemoveAllKeys() throws IOException { public void testRemoveAllKeys() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
PGPSecretKeyRing expected = PGPainless.readKeyRing().secretKeyRing(ALL_KEYS_REMOVED); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
OpenPGPKey expected = reader.parseKey(ALL_KEYS_REMOVED);
PGPSecretKeyRing removedSecretKeys = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing removedSecretKeys = GnuPGDummyKeyUtil.modify(secretKeys)
.removePrivateKeys(GnuPGDummyKeyUtil.KeyFilter.any()); .removePrivateKeys(GnuPGDummyKeyUtil.KeyFilter.any());
@ -240,33 +247,35 @@ public class GnuPGDummyKeyUtilTest {
assertEquals(GnuPGDummyExtension.NO_PRIVATE_KEY.getId(), s2k.getProtectionMode()); assertEquals(GnuPGDummyExtension.NO_PRIVATE_KEY.getId(), s2k.getProtectionMode());
} }
assertArrayEquals(expected.getEncoded(), removedSecretKeys.getEncoded()); assertArrayEquals(expected.getPGPSecretKeyRing().getEncoded(), removedSecretKeys.getEncoded());
} }
@Test @Test
public void testGetSingleIdOfHardwareBackedKey() throws IOException { public void testGetSingleIdOfHardwareBackedKey() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
assertTrue(GnuPGDummyKeyUtil.getIdsOfKeysWithGnuPGS2KDivertedToCard(secretKeys).isEmpty()); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
assertTrue(GnuPGDummyKeyUtil.getIdsOfKeysWithGnuPGS2KDivertedToCard(secretKeys.getPGPSecretKeyRing()).isEmpty());
PGPSecretKeyRing withHardwareBackedEncryptionKey = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing withHardwareBackedEncryptionKey = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(encryptionKeyId)); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.only(encryptionKeyId));
Set<SubkeyIdentifier> hardwareBackedKeys = GnuPGDummyKeyUtil Set<SubkeyIdentifier> hardwareBackedKeys = GnuPGDummyKeyUtil
.getIdsOfKeysWithGnuPGS2KDivertedToCard(withHardwareBackedEncryptionKey); .getIdsOfKeysWithGnuPGS2KDivertedToCard(withHardwareBackedEncryptionKey);
assertEquals(Collections.singleton(new SubkeyIdentifier(secretKeys, encryptionKeyId)), hardwareBackedKeys); assertEquals(Collections.singleton(new SubkeyIdentifier(secretKeys.getPGPSecretKeyRing(), encryptionKeyId)), hardwareBackedKeys);
} }
@Test @Test
public void testGetIdsOfFullyHardwareBackedKey() throws IOException { public void testGetIdsOfFullyHardwareBackedKey() throws IOException {
PGPSecretKeyRing secretKeys = PGPainless.readKeyRing().secretKeyRing(FULL_KEY); OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
assertTrue(GnuPGDummyKeyUtil.getIdsOfKeysWithGnuPGS2KDivertedToCard(secretKeys).isEmpty()); OpenPGPKey secretKeys = reader.parseKey(FULL_KEY);
assertTrue(GnuPGDummyKeyUtil.getIdsOfKeysWithGnuPGS2KDivertedToCard(secretKeys.getPGPSecretKeyRing()).isEmpty());
PGPSecretKeyRing withHardwareBackedEncryptionKey = GnuPGDummyKeyUtil.modify(secretKeys) PGPSecretKeyRing withHardwareBackedEncryptionKey = GnuPGDummyKeyUtil.modify(secretKeys)
.divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.any()); .divertPrivateKeysToCard(GnuPGDummyKeyUtil.KeyFilter.any());
Set<SubkeyIdentifier> expected = new HashSet<>(); Set<SubkeyIdentifier> expected = new HashSet<>();
for (PGPSecretKey key : secretKeys) { for (PGPSecretKey key : secretKeys.getPGPSecretKeyRing()) {
expected.add(new SubkeyIdentifier(secretKeys, key.getKeyID())); expected.add(new SubkeyIdentifier(secretKeys.getPGPSecretKeyRing(), key.getKeyID()));
} }
Set<SubkeyIdentifier> hardwareBackedKeys = GnuPGDummyKeyUtil Set<SubkeyIdentifier> hardwareBackedKeys = GnuPGDummyKeyUtil

View file

@ -15,8 +15,9 @@ import java.io.IOException;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPublicKeyRing; import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.PGPSecretKeyRing; import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import org.bouncycastle.util.io.Streams; import org.bouncycastle.util.io.Streams;
import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test; import org.junit.jupiter.api.Test;
@ -134,15 +135,16 @@ public class DecryptOrVerify {
"=9PiO\n" + "=9PiO\n" +
"-----END PGP MESSAGE-----"; "-----END PGP MESSAGE-----";
private static PGPSecretKeyRing secretKey; private static OpenPGPKey secretKey;
private static PGPPublicKeyRing certificate; private static OpenPGPCertificate certificate;
@BeforeAll @BeforeAll
public static void prepare() throws IOException { public static void prepare() throws IOException {
OpenPGPKeyReader reader = PGPainless.getInstance().readKey();
// read the secret key // read the secret key
secretKey = PGPainless.readKeyRing().secretKeyRing(KEY); secretKey = reader.parseKey(KEY);
// certificate is the public part of the key // certificate is the public part of the key
certificate = PGPainless.extractCertificate(secretKey); certificate = secretKey.toCertificate();
} }
/** /**
@ -153,7 +155,7 @@ public class DecryptOrVerify {
*/ */
@Test @Test
public void decryptMessage() throws PGPException, IOException { public void decryptMessage() throws PGPException, IOException {
ConsumerOptions consumerOptions = new ConsumerOptions() ConsumerOptions consumerOptions = ConsumerOptions.get()
.addDecryptionKey(secretKey, keyProtector); // add the decryption key ring .addDecryptionKey(secretKey, keyProtector); // add the decryption key ring
ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream(); ByteArrayOutputStream plaintextOut = new ByteArrayOutputStream();
@ -186,7 +188,7 @@ public class DecryptOrVerify {
*/ */
@Test @Test
public void decryptMessageAndVerifySignatures() throws PGPException, IOException { public void decryptMessageAndVerifySignatures() throws PGPException, IOException {
ConsumerOptions consumerOptions = new ConsumerOptions() ConsumerOptions consumerOptions = ConsumerOptions.get()
.addDecryptionKey(secretKey, keyProtector) // provide the secret key of the recipient for decryption .addDecryptionKey(secretKey, keyProtector) // provide the secret key of the recipient for decryption
.addVerificationCert(certificate); // provide the signers public key for signature verification .addVerificationCert(certificate); // provide the signers public key for signature verification
@ -218,7 +220,7 @@ public class DecryptOrVerify {
*/ */
@Test @Test
public void verifySignatures() throws PGPException, IOException { public void verifySignatures() throws PGPException, IOException {
ConsumerOptions options = new ConsumerOptions() ConsumerOptions options = ConsumerOptions.get()
.addVerificationCert(certificate); // provide the signers certificate for verification of signatures .addVerificationCert(certificate); // provide the signers certificate for verification of signatures
for (String signed : new String[] {INBAND_SIGNED, CLEARTEXT_SIGNED}) { for (String signed : new String[] {INBAND_SIGNED, CLEARTEXT_SIGNED}) {
@ -257,7 +259,7 @@ public class DecryptOrVerify {
SigningOptions signingOptions = SigningOptions.get(); SigningOptions signingOptions = SigningOptions.get();
// for cleartext signed messages, we need to add a detached signature... // for cleartext signed messages, we need to add a detached signature...
signingOptions.addDetachedSignature(keyProtector, secretKey, DocumentSignatureType.CANONICAL_TEXT_DOCUMENT); signingOptions.addDetachedSignature(keyProtector, secretKey.getPGPSecretKeyRing(), DocumentSignatureType.CANONICAL_TEXT_DOCUMENT);
ProducerOptions producerOptions = ProducerOptions.sign(signingOptions) ProducerOptions producerOptions = ProducerOptions.sign(signingOptions)
.setCleartextSigned(); // and declare that the message will be cleartext signed .setCleartextSigned(); // and declare that the message will be cleartext signed
@ -279,7 +281,7 @@ public class DecryptOrVerify {
// and pass it to the decryption stream // and pass it to the decryption stream
DecryptionStream verificationStream = PGPainless.decryptAndOrVerify() DecryptionStream verificationStream = PGPainless.decryptAndOrVerify()
.onInputStream(signedIn) .onInputStream(signedIn)
.withOptions(new ConsumerOptions().addVerificationCert(certificate)); .withOptions(ConsumerOptions.get().addVerificationCert(certificate));
// plain will receive the plaintext message // plain will receive the plaintext message
ByteArrayOutputStream plain = new ByteArrayOutputStream(); ByteArrayOutputStream plain = new ByteArrayOutputStream();