From 883eb80a63381e310bf96390ac72d856266aa8f0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Tue, 11 Mar 2025 22:04:03 +0100 Subject: [PATCH] Bump bcpg, bcprov to 1.80, add bcutil dependency Adding bcutil as a dependency is apparently required now. See https://github.com/bcgit/bc-java/issues/1977 --- pgpainless-core/build.gradle | 1 + .../CachingBcPublicKeyDataDecryptorFactory.kt | 33 ++++++++++++++++--- .../CustomPublicKeyDataDecryptorFactory.kt | 5 +-- .../HardwareSecurity.kt | 16 ++++++--- ...stomPublicKeyDataDecryptorFactoryTest.java | 4 +-- version.gradle | 2 +- 6 files changed, 47 insertions(+), 14 deletions(-) diff --git a/pgpainless-core/build.gradle b/pgpainless-core/build.gradle index bab6ecf1..64d538d5 100644 --- a/pgpainless-core/build.gradle +++ b/pgpainless-core/build.gradle @@ -22,6 +22,7 @@ dependencies { // Bouncy Castle api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion" api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion" + api "org.bouncycastle:bcutil-jdk18on:$bouncyCastleVersion" // api(files("../libs/bcpg-jdk18on-1.70.jar")) // @Nullable, @Nonnull annotations diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt index 3d07065e..9bafa6da 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CachingBcPublicKeyDataDecryptorFactory.kt @@ -4,7 +4,11 @@ package org.pgpainless.decryption_verification +import org.bouncycastle.bcpg.AEADEncDataPacket +import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket import org.bouncycastle.openpgp.PGPPrivateKey +import org.bouncycastle.openpgp.PGPSessionKey +import org.bouncycastle.openpgp.operator.PGPDataDecryptor import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory import org.bouncycastle.util.encoders.Base64 import org.pgpainless.key.SubkeyIdentifier @@ -21,16 +25,34 @@ import org.pgpainless.key.SubkeyIdentifier class CachingBcPublicKeyDataDecryptorFactory( privateKey: PGPPrivateKey, override val subkeyIdentifier: SubkeyIdentifier -) : BcPublicKeyDataDecryptorFactory(privateKey), CustomPublicKeyDataDecryptorFactory { +) : CustomPublicKeyDataDecryptorFactory() { + private val decryptorFactory: BcPublicKeyDataDecryptorFactory = + BcPublicKeyDataDecryptorFactory(privateKey) private val cachedSessions: MutableMap = mutableMapOf() + override fun createDataDecryptor(p0: Boolean, p1: Int, p2: ByteArray?): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1, p2) + } + + override fun createDataDecryptor(p0: AEADEncDataPacket?, p1: PGPSessionKey?): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1) + } + + override fun createDataDecryptor( + p0: SymmetricEncIntegrityPacket?, + p1: PGPSessionKey? + ): PGPDataDecryptor { + return decryptorFactory.createDataDecryptor(p0, p1) + } + override fun recoverSessionData( keyAlgorithm: Int, - secKeyData: Array + secKeyData: Array, + pkeskVersion: Int ): ByteArray = lookupSessionKeyData(secKeyData) - ?: costlyRecoverSessionData(keyAlgorithm, secKeyData).also { + ?: costlyRecoverSessionData(keyAlgorithm, secKeyData, pkeskVersion).also { cacheSessionKeyData(secKeyData, it) } @@ -39,8 +61,9 @@ class CachingBcPublicKeyDataDecryptorFactory( private fun costlyRecoverSessionData( keyAlgorithm: Int, - secKeyData: Array - ): ByteArray = super.recoverSessionData(keyAlgorithm, secKeyData) + secKeyData: Array, + pkeskVersion: Int + ): ByteArray = decryptorFactory.recoverSessionData(keyAlgorithm, secKeyData, pkeskVersion) private fun cacheSessionKeyData(secKeyData: Array, sessionKey: ByteArray) { cachedSessions[toKey(secKeyData)] = sessionKey.clone() diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt index 4a0dbeba..cb6254dc 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactory.kt @@ -4,6 +4,7 @@ package org.pgpainless.decryption_verification +import org.bouncycastle.openpgp.operator.AbstractPublicKeyDataDecryptorFactory import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory import org.pgpainless.key.SubkeyIdentifier @@ -14,7 +15,7 @@ import org.pgpainless.key.SubkeyIdentifier * * @see [ConsumerOptions.addCustomDecryptorFactory] */ -interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory { +abstract class CustomPublicKeyDataDecryptorFactory : AbstractPublicKeyDataDecryptorFactory() { /** * Identifier for the subkey for which this particular [CustomPublicKeyDataDecryptorFactory] is @@ -22,5 +23,5 @@ interface CustomPublicKeyDataDecryptorFactory : PublicKeyDataDecryptorFactory { * * @return subkey identifier */ - val subkeyIdentifier: SubkeyIdentifier + abstract val subkeyIdentifier: SubkeyIdentifier } diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt index 1974e290..50ef3e02 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/HardwareSecurity.kt @@ -29,11 +29,17 @@ class HardwareSecurity { * @param keyId id of the key * @param keyAlgorithm algorithm * @param sessionKeyData encrypted session key + * @param pkeskVersion version of the Public-Key-Encrypted-Session-Key packet (3 or 6) * @return decrypted session key * @throws HardwareSecurityException exception */ @Throws(HardwareSecurityException::class) - fun decryptSessionKey(keyId: Long, keyAlgorithm: Int, sessionKeyData: ByteArray): ByteArray + fun decryptSessionKey( + keyId: Long, + keyAlgorithm: Int, + sessionKeyData: ByteArray, + pkeskVersion: Int + ): ByteArray } /** @@ -44,7 +50,7 @@ class HardwareSecurity { class HardwareDataDecryptorFactory( override val subkeyIdentifier: SubkeyIdentifier, private val callback: DecryptionCallback, - ) : CustomPublicKeyDataDecryptorFactory { + ) : CustomPublicKeyDataDecryptorFactory() { // luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument. private val factory: PublicKeyDataDecryptorFactory = BcPublicKeyDataDecryptorFactory(null) @@ -73,10 +79,12 @@ class HardwareSecurity { override fun recoverSessionData( keyAlgorithm: Int, - secKeyData: Array + secKeyData: Array, + pkeskVersion: Int ): ByteArray { return try { - callback.decryptSessionKey(subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0]) + callback.decryptSessionKey( + subkeyIdentifier.subkeyId, keyAlgorithm, secKeyData[0], pkeskVersion) } catch (e: HardwareSecurityException) { throw PGPException("Hardware-backed decryption failed.", e) } diff --git a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java index 73c3bf56..71fbf9be 100644 --- a/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java +++ b/pgpainless-core/src/test/java/org/pgpainless/decryption_verification/CustomPublicKeyDataDecryptorFactoryTest.java @@ -55,14 +55,14 @@ public class CustomPublicKeyDataDecryptorFactoryTest { HardwareSecurity.DecryptionCallback hardwareDecryptionCallback = new HardwareSecurity.DecryptionCallback() { @Override - public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData) + public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion) throws HardwareSecurity.HardwareSecurityException { // Emulate hardware decryption. try { PGPSecretKey decryptionKey = secretKey.getSecretKey(encryptionKey.getKeyID()); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, Passphrase.emptyPassphrase()); PublicKeyDataDecryptorFactory internal = new BcPublicKeyDataDecryptorFactory(privateKey); - return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}); + return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}, pkeskVersion); } catch (PGPException e) { throw new HardwareSecurity.HardwareSecurityException(); } diff --git a/version.gradle b/version.gradle index 4d36ca57..25dae7be 100644 --- a/version.gradle +++ b/version.gradle @@ -8,7 +8,7 @@ allprojects { isSnapshot = true pgpainlessMinAndroidSdk = 10 javaSourceCompatibility = 1.8 - bouncyCastleVersion = '1.78.1' + bouncyCastleVersion = '1.80' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' logbackVersion = '1.5.13'