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:
parent
739070681f
commit
001acb55ee
8 changed files with 46 additions and 21 deletions
|
@ -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"))
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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'
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue