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
api "org.bouncycastle:bcprov-jdk18on:$bouncyCastleVersion"
api "org.bouncycastle:bcutil-jdk18on:$bouncyPgVersion"
api "org.bouncycastle:bcpg-jdk18on:$bouncyPgVersion"
// api(files("../libs/bcpg-jdk18on-1.70.jar"))

View file

@ -4,8 +4,12 @@
package org.pgpainless.decryption_verification;
import org.bouncycastle.bcpg.AEADEncDataPacket;
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
import org.bouncycastle.openpgp.PGPException;
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.bc.BcPublicKeyDataDecryptorFactory;
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.
*/
public class CachingBcPublicKeyDataDecryptorFactory
extends BcPublicKeyDataDecryptorFactory
implements CustomPublicKeyDataDecryptorFactory {
extends CustomPublicKeyDataDecryptorFactory {
private static final Logger LOGGER = LoggerFactory.getLogger(CachingBcPublicKeyDataDecryptorFactory.class);
private final BcPublicKeyDataDecryptorFactory decryptorFactory;
private final Map<String, byte[]> cachedSessionKeys = new HashMap<>();
private final SubkeyIdentifier decryptionKey;
public CachingBcPublicKeyDataDecryptorFactory(PGPPrivateKey privateKey, SubkeyIdentifier decryptionKey) {
super(privateKey);
this.decryptorFactory = new BcPublicKeyDataDecryptorFactory(privateKey);
this.decryptionKey = decryptionKey;
}
@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);
if (sessionKey == null) {
LOGGER.debug("Cache miss for encrypted session key " + Hex.toHexString(secKeyData[0]));
sessionKey = costlyRecoverSessionData(keyAlgorithm, secKeyData);
sessionKey = costlyRecoverSessionData(keyAlgorithm, secKeyData, pkeskVersion);
cacheSessionKeyData(secKeyData, sessionKey);
} else {
LOGGER.debug("Cache hit for encrypted session key " + Hex.toHexString(secKeyData[0]));
@ -53,8 +57,8 @@ public class CachingBcPublicKeyDataDecryptorFactory
return sessionKey;
}
public byte[] costlyRecoverSessionData(int keyAlgorithm, byte[][] secKeyData) throws PGPException {
return super.recoverSessionData(keyAlgorithm, secKeyData);
public byte[] costlyRecoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion) throws PGPException {
return decryptorFactory.recoverSessionData(keyAlgorithm, secKeyData, pkeskVersion);
}
private byte[] lookupSessionKeyData(byte[][] secKeyData) {
@ -91,4 +95,19 @@ public class CachingBcPublicKeyDataDecryptorFactory
public SubkeyIdentifier getSubkeyIdentifier() {
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;
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;
* TPMs.
* @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}
@ -22,6 +23,6 @@ public interface CustomPublicKeyDataDecryptorFactory extends PublicKeyDataDecryp
*
* @return subkey identifier
*/
SubkeyIdentifier getSubkeyIdentifier();
abstract SubkeyIdentifier getSubkeyIdentifier();
}

View file

@ -29,11 +29,12 @@ public class HardwareSecurity {
* @param keyId id of the key
* @param keyAlgorithm algorithm
* @param sessionKeyData encrypted session key
* @param pkeskVersion Public-Key Encrypted Session-Key Packet version
*
* @return decrypted session key
* @throws HardwareSecurityException exception
*/
byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData)
byte[] decryptSessionKey(long keyId, int keyAlgorithm, byte[] sessionKeyData, int pkeskVersion)
throws HardwareSecurityException;
}
@ -43,7 +44,7 @@ public class HardwareSecurity {
* to a {@link DecryptionCallback}.
* 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;
// luckily we can instantiate the BcPublicKeyDataDecryptorFactory with null as argument.
@ -63,11 +64,11 @@ public class HardwareSecurity {
}
@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException {
try {
// 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) {
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 {
syntaxVerifier.next(InputSymbol.OnePassSignature);
PGPOnePassSignature onePassSignature = packetInputStream.readOnePassSignature();
LOGGER.debug("One-Pass-Signature Packet by key " + KeyIdUtil.formatKeyId(onePassSignature.getKeyID()) +
" at depth " + metadata.depth + " encountered");
signatures.addOnePassSignature(onePassSignature);
try {
PGPOnePassSignature onePassSignature = packetInputStream.readOnePassSignature();
LOGGER.debug("One-Pass-Signature Packet by key " + KeyIdUtil.formatKeyId(onePassSignature.getKeyID()) +
" 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 {

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
_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
_SECP256K1("secp256k1", 256),
_BRAINPOOLP256R1("brainpoolP256r1", 256),
_BRAINPOOLP384R1("brainpoolP384r1", 384),
_BRAINPOOLP512R1("brainpoolP512r1", 512)

View file

@ -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();
}

View file

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