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

Bump BC to 1.80, apply required code changes

We had to rework the HardwareSecurity decryptor factory classes, which now take a PKESK version as additional argument
Further, we had to remove support for generating secp256k1 EC keys.
Lastly, parsing One-Pass-Signatures of unsupported versions in BC now throws, so we had to
introduce a respective catch block.
This commit is contained in:
Paul Schaub 2025-03-28 18:28:56 +01:00
parent 739070681f
commit 001acb55ee
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
8 changed files with 46 additions and 21 deletions

View file

@ -21,6 +21,7 @@ dependencies {
// Bouncy Castle // Bouncy Castle
api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion" api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion"
api "org.bouncycastle:bcutil-jdk18on:$bouncyPgVersion"
api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion" api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion"
// api(files("../libs/bcpg-jdk18on-1.70.jar")) // api(files("../libs/bcpg-jdk18on-1.70.jar"))

View file

@ -4,8 +4,12 @@
package org.pgpainless.decryption_verification; package org.pgpainless.decryption_verification;
import org.bouncycastle.bcpg.AEADEncDataPacket;
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
import org.bouncycastle.openpgp.PGPException; import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPPrivateKey; import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPSessionKey;
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.bouncycastle.util.encoders.Base64; import org.bouncycastle.util.encoders.Base64;
@ -27,25 +31,25 @@ import java.util.Map;
* The result of that is then placed in the cache and returned. * The result of that is then placed in the cache and returned.
*/ */
public class CachingBcPublicKeyDataDecryptorFactory public class CachingBcPublicKeyDataDecryptorFactory
extends BcPublicKeyDataDecryptorFactory extends CustomPublicKeyDataDecryptorFactory {
implements CustomPublicKeyDataDecryptorFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(CachingBcPublicKeyDataDecryptorFactory.class); private static final Logger LOGGER = LoggerFactory.getLogger(CachingBcPublicKeyDataDecryptorFactory.class);
private final BcPublicKeyDataDecryptorFactory decryptorFactory;
private final Map<String, byte[]> cachedSessionKeys = new HashMap<>(); private final Map<String, byte[]> cachedSessionKeys = new HashMap<>();
private final SubkeyIdentifier decryptionKey; private final SubkeyIdentifier decryptionKey;
public CachingBcPublicKeyDataDecryptorFactory(PGPPrivateKey privateKey, SubkeyIdentifier decryptionKey) { public CachingBcPublicKeyDataDecryptorFactory(PGPPrivateKey privateKey, SubkeyIdentifier decryptionKey) {
super(privateKey); this.decryptorFactory = new BcPublicKeyDataDecryptorFactory(privateKey);
this.decryptionKey = decryptionKey; this.decryptionKey = decryptionKey;
} }
@Override @Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException { public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion) throws PGPException {
byte[] sessionKey = lookupSessionKeyData(secKeyData); byte[] sessionKey = lookupSessionKeyData(secKeyData);
if (sessionKey == null) { if (sessionKey == null) {
LOGGER.debug("Cache miss for encrypted session key " + Hex.toHexString(secKeyData[0])); LOGGER.debug("Cache miss for encrypted session key " + Hex.toHexString(secKeyData[0]));
sessionKey = costlyRecoverSessionData(keyAlgorithm, secKeyData); sessionKey = costlyRecoverSessionData(keyAlgorithm, secKeyData, pkeskVersion);
cacheSessionKeyData(secKeyData, sessionKey); cacheSessionKeyData(secKeyData, sessionKey);
} else { } else {
LOGGER.debug("Cache hit for encrypted session key " + Hex.toHexString(secKeyData[0])); LOGGER.debug("Cache hit for encrypted session key " + Hex.toHexString(secKeyData[0]));
@ -53,8 +57,8 @@ public class CachingBcPublicKeyDataDecryptorFactory
return sessionKey; return sessionKey;
} }
public byte[] costlyRecoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException { public byte[] costlyRecoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion) throws PGPException {
return super.recoverSessionData(keyAlgorithm, secKeyData); return decryptorFactory.recoverSessionData(keyAlgorithm, secKeyData, pkeskVersion);
} }
private byte[] lookupSessionKeyData(byte[][] secKeyData) { private byte[] lookupSessionKeyData(byte[][] secKeyData) {
@ -91,4 +95,19 @@ public class CachingBcPublicKeyDataDecryptorFactory
public SubkeyIdentifier getSubkeyIdentifier() { public SubkeyIdentifier getSubkeyIdentifier() {
return decryptionKey; return decryptionKey;
} }
@Override
public PGPDataDecryptor createDataDecryptor(boolean b, int i, byte[] bytes) throws PGPException {
return decryptorFactory.createDataDecryptor(b, i, bytes);
}
@Override
public PGPDataDecryptor createDataDecryptor(AEADEncDataPacket aeadEncDataPacket, PGPSessionKey pgpSessionKey) throws PGPException {
return decryptorFactory.createDataDecryptor(aeadEncDataPacket, pgpSessionKey);
}
@Override
public PGPDataDecryptor createDataDecryptor(SymmetricEncIntegrityPacket symmetricEncIntegrityPacket, PGPSessionKey pgpSessionKey) throws PGPException {
return decryptorFactory.createDataDecryptor(symmetricEncIntegrityPacket, pgpSessionKey);
}
} }

View file

@ -4,6 +4,7 @@
package org.pgpainless.decryption_verification; package org.pgpainless.decryption_verification;
import org.bouncycastle.openpgp.operator.AbstractPublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory; import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.pgpainless.key.SubkeyIdentifier; import org.pgpainless.key.SubkeyIdentifier;
@ -14,7 +15,7 @@ import org.pgpainless.key.SubkeyIdentifier;
* TPMs. * TPMs.
* @see ConsumerOptions#addCustomDecryptorFactory(CustomPublicKeyDataDecryptorFactory) * @see ConsumerOptions#addCustomDecryptorFactory(CustomPublicKeyDataDecryptorFactory)
*/ */
public interface CustomPublicKeyDataDecryptorFactory extends PublicKeyDataDecryptorFactory { public abstract class CustomPublicKeyDataDecryptorFactory extends AbstractPublicKeyDataDecryptorFactory {
/** /**
* Return the {@link SubkeyIdentifier} for which this particular {@link CustomPublicKeyDataDecryptorFactory} * Return the {@link SubkeyIdentifier} for which this particular {@link CustomPublicKeyDataDecryptorFactory}
@ -22,6 +23,6 @@ public interface CustomPublicKeyDataDecryptorFactory extends PublicKeyDataDecryp
* *
* @return subkey identifier * @return subkey identifier
*/ */
SubkeyIdentifier getSubkeyIdentifier(); abstract SubkeyIdentifier getSubkeyIdentifier();
} }

View file

@ -29,11 +29,12 @@ public class HardwareSecurity {
* @param keyId id of the key * @param keyId id of the key
* @param keyAlgorithm algorithm * @param keyAlgorithm algorithm
* @param sessionKeyData encrypted session key * @param sessionKeyData encrypted session key
* @param pkeskVersion Public-Key Encrypted Session-Key Packet version
* *
* @return decrypted session key * @return decrypted session key
* @throws HardwareSecurityException exception * @throws HardwareSecurityException exception
*/ */
byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData) byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion)
throws HardwareSecurityException; throws HardwareSecurityException;
} }
@ -43,7 +44,7 @@ public class HardwareSecurity {
* to a {@link DecryptionCallback}. * to a {@link DecryptionCallback}.
* Users can provide such a callback to delegate decryption of messages to hardware security SDKs. * Users can provide such a callback to delegate decryption of messages to hardware security SDKs.
*/ */
public static class HardwareDataDecryptorFactory implements CustomPublicKeyDataDecryptorFactory { public static class HardwareDataDecryptorFactory extends CustomPublicKeyDataDecryptorFactory {
private final DecryptionCallback callback; private final DecryptionCallback callback;
// luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument. // luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument.
@ -63,11 +64,11 @@ public class HardwareSecurity {
} }
@Override @Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData) public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException { throws PGPException {
try { try {
// delegate decryption to the callback // delegate decryption to the callback
return callback.decryptSessionKey(subkey.getSubkeyId(), keyAlgorithm, secKeyData[0]); return callback.decryptSessionKey(subkey.getSubkeyId(), keyAlgorithm, secKeyData[0], pkeskVersion);
} catch (HardwareSecurityException e) { } catch (HardwareSecurityException e) {
throw new PGPException("Hardware-backed decryption failed.", e); throw new PGPException("Hardware-backed decryption failed.", e);
} }

View file

@ -369,10 +369,14 @@ public class OpenPgpMessageInputStream extends DecryptionStream {
private void processOnePassSignature() throws PGPException, IOException { private void processOnePassSignature() throws PGPException, IOException {
syntaxVerifier.next(InputSymbol.OnePassSignature); syntaxVerifier.next(InputSymbol.OnePassSignature);
PGPOnePassSignature onePassSignature = packetInputStream.readOnePassSignature(); try {
LOGGER.debug("One-Pass-Signature Packet by key " + KeyIdUtil.formatKeyId(onePassSignature.getKeyID()) + PGPOnePassSignature onePassSignature = packetInputStream.readOnePassSignature();
" at depth " + metadata.depth + " encountered"); LOGGER.debug("One-Pass-Signature Packet by key " + KeyIdUtil.formatKeyId(onePassSignature.getKeyID()) +
signatures.addOnePassSignature(onePassSignature); " at depth " + metadata.depth + " encountered");
signatures.addOnePassSignature(onePassSignature);
} catch (UnsupportedPacketVersionException e) {
LOGGER.debug("Ignoring One-Pass-Signature Packet of unsupported version.", e);
}
} }
private void processSignature() throws PGPException, IOException { private void processSignature() throws PGPException, IOException {

View file

@ -18,7 +18,6 @@ public enum EllipticCurve {
_P256("prime256v1", 256), // prime256v1 is equivalent to P-256, see https://tools.ietf.org/search/rfc4492#page-32 _P256("prime256v1", 256), // prime256v1 is equivalent to P-256, see https://tools.ietf.org/search/rfc4492#page-32
_P384("secp384r1", 384), // secp384r1 is equivalent to P-384, see https://tools.ietf.org/search/rfc4492#page-32 _P384("secp384r1", 384), // secp384r1 is equivalent to P-384, see https://tools.ietf.org/search/rfc4492#page-32
_P521("secp521r1", 521), // secp521r1 is equivalent to P-521, see https://tools.ietf.org/search/rfc4492#page-32 _P521("secp521r1", 521), // secp521r1 is equivalent to P-521, see https://tools.ietf.org/search/rfc4492#page-32
_SECP256K1("secp256k1", 256),
_BRAINPOOLP256R1("brainpoolP256r1", 256), _BRAINPOOLP256R1("brainpoolP256r1", 256),
_BRAINPOOLP384R1("brainpoolP384r1", 384), _BRAINPOOLP384R1("brainpoolP384r1", 384),
_BRAINPOOLP512R1("brainpoolP512r1", 512) _BRAINPOOLP512R1("brainpoolP512r1", 512)

View file

@ -55,14 +55,14 @@ public class CustomPublicKeyDataDecryptorFactoryTest {
HardwareSecurity.DecryptionCallback hardwareDecryptionCallback = new HardwareSecurity.DecryptionCallback() { HardwareSecurity.DecryptionCallback hardwareDecryptionCallback = new HardwareSecurity.DecryptionCallback() {
@Override @Override
public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData) public byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion)
throws HardwareSecurity.HardwareSecurityException { throws HardwareSecurity.HardwareSecurityException {
// Emulate hardware decryption. // Emulate hardware decryption.
try { try {
PGPSecretKey decryptionKey = secretKey.getSecretKey(encryptionKey.getKeyID()); PGPSecretKey decryptionKey = secretKey.getSecretKey(encryptionKey.getKeyID());
PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, Passphrase.emptyPassphrase()); PGPPrivateKey privateKey = UnlockSecretKey.unlockSecretKey(decryptionKey, Passphrase.emptyPassphrase());
PublicKeyDataDecryptorFactory internal = new BcPublicKeyDataDecryptorFactory(privateKey); PublicKeyDataDecryptorFactory internal = new BcPublicKeyDataDecryptorFactory(privateKey);
return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}); return internal.recoverSessionData(keyAlgorithm, new byte[][] {sessionKeyData}, pkeskVersion);
} catch (PGPException e) { } catch (PGPException e) {
throw new HardwareSecurity.HardwareSecurityException(); throw new HardwareSecurity.HardwareSecurityException();
} }

View file

@ -8,7 +8,7 @@ allprojects {
isSnapshot = true isSnapshot = true
pgpainlessMinAndroidSdk = 10 pgpainlessMinAndroidSdk = 10
javaSourceCompatibility = 1.8 javaSourceCompatibility = 1.8
bouncyCastleVersion = '1.77' bouncyCastleVersion = '1.80'
bouncyPgVersion = bouncyCastleVersion bouncyPgVersion = bouncyCastleVersion
junitVersion = '5.8.2' junitVersion = '5.8.2'
logbackVersion = '1.2.13' logbackVersion = '1.2.13'