diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java
index 324a5042ea..e18b5e60ff 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java
@@ -7,6 +7,7 @@
import org.bouncycastle.bcpg.InputStreamPacket;
import org.bouncycastle.bcpg.PublicKeyAlgorithmTags;
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
+import org.bouncycastle.bcpg.SymmetricEncDataPacket;
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
@@ -110,6 +111,18 @@ public PGPSessionKey getSessionKey(
PublicKeyDataDecryptorFactory dataDecryptorFactory)
throws PGPException
{
+ if (encData instanceof AEADEncDataPacket)
+ {
+ return dataDecryptorFactory.recoverSessionKey(keyData, (AEADEncDataPacket) encData);
+ }
+
+ if (encData instanceof SymmetricEncDataPacket)
+ {
+ return dataDecryptorFactory.recoverSessionKey(keyData, (SymmetricEncDataPacket) encData);
+ }
+
+ return dataDecryptorFactory.recoverSessionKey(keyData, (SymmetricEncIntegrityPacket) encData);
+
byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey(), getVersion());
if (keyData.getAlgorithm() == PublicKeyAlgorithmTags.X25519 || keyData.getAlgorithm() == PublicKeyAlgorithmTags.X448)
{
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java
index aef64a10b7..cc78cf17cb 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/PublicKeyDataDecryptorFactory.java
@@ -1,10 +1,37 @@
package org.bouncycastle.openpgp.operator;
+import org.bouncycastle.bcpg.InputStreamPacket;
+import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
import org.bouncycastle.openpgp.PGPException;
public interface PublicKeyDataDecryptorFactory
extends PGPDataDecryptorFactory
{
+ /**
+ * Recover the session key.
+ * The return value is a two-dimensional byte array containing the following information about the session key:
+ *
+ * - session-key
+ * - 1 or 0 bytes encoding the optionally included symmetric key algorithm
+ * - 0 or 2 checksum bytes
+ *
+ * @param pkesk public key encrypted session-key packet
+ * @param encryptedData SEIPD, SED or OED packet
+ * @return 2-dimensional byte array.
+ * @throws PGPException
+ */
+ byte[][] recoverSessionKey(PublicKeyEncSessionPacket pkesk, InputStreamPacket encryptedData)
+ throws PGPException;
+
+ /**
+ * @deprecated use {@link #recoverSessionKey(PublicKeyEncSessionPacket, InputStreamPacket)} instead.
+ * @param keyAlgorithm public key algorithm
+ * @param secKeyData encrypted session key data
+ * @param pkeskVersion version of the PKESK packet
+ * @return
+ * @throws PGPException
+ */
byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException;
+
}
diff --git a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
index 2f8dc4f228..d779787c17 100644
--- a/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
+++ b/pg/src/main/java/org/bouncycastle/openpgp/operator/bc/BcPublicKeyDataDecryptorFactory.java
@@ -29,7 +29,6 @@
import org.bouncycastle.openpgp.operator.PGPPad;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.RFC6637Utils;
-import org.bouncycastle.pqc.crypto.gemss.GeMSSParameters;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.BigIntegers;
@@ -48,6 +47,98 @@ public BcPublicKeyDataDecryptorFactory(PGPPrivateKey pgpPrivKey)
this.pgpPrivKey = pgpPrivKey;
}
+ @Override
+ public byte[][] recoverSessionKey(PublicKeyEncSessionPacket pkesk, InputStreamPacket encData)
+ throws PGPException
+ {
+ if (pkesk.getAlgorithm() != pgpPrivKey.getPublicKeyPacket().getAlgorithm())
+ {
+ throw new PGPException("Public-Key algorithm field of the Public-Key Encrypted Session Key Packet" +
+ " does not match the private keys algorithm.");
+ }
+
+ byte[][] secKeyData = pkesk.getEncSessionKey();
+
+ try
+ {
+ AsymmetricKeyParameter privKey = KEY_CONVERTER.getPrivateKey(pgpPrivKey);
+ int keyAlgorithm = pkesk.getAlgorithm();
+ switch (keyAlgorithm)
+ {
+ case PublicKeyAlgorithmTags.X25519:
+
+ case PublicKeyAlgorithmTags.X448:
+ return recoverXSessionData(secKeyData[0], privKey, X448PublicBCPGKey.LENGTH,
+ HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_256, new X448Agreement(),
+ "X448", new PublicKeyParametersOperation()
+ {
+ @Override
+ public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff)
+ {
+ return new X448PublicKeyParameters(pEnc, 0);
+ }
+ }, pkesk.getVersion());
+
+ case PublicKeyAlgorithmTags.ECDH:
+ byte[] enc = secKeyData[0];
+ byte[] pEnc;
+ byte[] keyEnc;
+ int pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
+ assertOutOfRange(2 + pLen + 1, enc);
+
+ pEnc = new byte[pLen];
+ System.arraycopy(enc, 2, pEnc, 0, pLen);
+
+ int keyLen = enc[pLen + 2] & 0xff;
+ assertOutOfRange(2 + pLen + 1 + keyLen, enc);
+
+ keyEnc = new byte[keyLen];
+ System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyLen);
+
+ byte[] secret;
+ RFC6637KDFCalculator rfc6637KDFCalculator;
+ byte[] userKeyingMaterial;
+ int symmetricKeyAlgorithm, hashAlgorithm;
+
+ ECDHPublicBCPGKey ecPubKey = (ECDHPublicBCPGKey)pgpPrivKey.getPublicKeyPacket().getKey();
+ // XDH
+ if (ecPubKey.getCurveOID().equals(CryptlibObjectIdentifiers.curvey25519))
+ {
+ if (pEnc.length != 1 + X25519PublicKeyParameters.KEY_SIZE || 0x40 != pEnc[0])
+ {
+ throw new IllegalArgumentException("Invalid Curve25519 public key");
+ }
+ // skip the 0x40 header byte.
+ secret = BcUtil.getSecret(new X25519Agreement(), privKey, new X25519PublicKeyParameters(pEnc, 1));
+ }
+ else
+ {
+ ECDomainParameters ecParameters = ((ECPrivateKeyParameters)privKey).getParameters();
+
+ ECPublicKeyParameters ephPub = new ECPublicKeyParameters(ecParameters.getCurve().decodePoint(pEnc),
+ ecParameters);
+
+ ECDHBasicAgreement agreement = new ECDHBasicAgreement();
+ agreement.init(privKey);
+ BigInteger S = agreement.calculateAgreement(ephPub);
+ secret = BigIntegers.asUnsignedByteArray(agreement.getFieldSize(), S);
+ }
+ hashAlgorithm = ecPubKey.getHashAlgorithm();
+ symmetricKeyAlgorithm = ecPubKey.getSymmetricKeyAlgorithm();
+ userKeyingMaterial = RFC6637Utils.createUserKeyingMaterial(pgpPrivKey.getPublicKeyPacket(), new BcKeyFingerprintCalculator());
+ rfc6637KDFCalculator = new RFC6637KDFCalculator(new BcPGPDigestCalculatorProvider().get(hashAlgorithm), symmetricKeyAlgorithm);
+ KeyParameter key = new KeyParameter(rfc6637KDFCalculator.createKey(secret, userKeyingMaterial));
+
+ return PGPPad.unpadSessionData(unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key));
+ }
+ } catch (InvalidCipherTextException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException
@@ -276,7 +367,7 @@ private byte[] recoverXSessionData2(byte[] ephemeralPK,
* @throws PGPException
* @throws InvalidCipherTextException
*/
- private byte[] recoverXSessionData(byte[] enc, AsymmetricKeyParameter privKey, int pLen, int hashAlgorithm, int symmetricKeyAlgorithm,
+ private byte[][] recoverXSessionData(byte[] enc, AsymmetricKeyParameter privKey, int pLen, int hashAlgorithm, int symmetricKeyAlgorithm,
RawAgreement agreement, String algorithmName, PublicKeyParametersOperation pkp, int pkeskVersion)
throws PGPException, InvalidCipherTextException
{
@@ -311,11 +402,19 @@ private byte[] recoverXSessionData(byte[] enc, AsymmetricKeyParameter privKey, i
if (pkeskVersion == 6)
{
- return unwrapped;
+ return new byte[][] {
+ unwrapped, // session key
+ new byte[0], // v6 pkesk does not contain sym-alg (sourced from seipd2 instead)
+ new byte[0] // X25519, X448 does not include checksum
+ };
}
else
{
- return Arrays.prepend(unwrapped, enc[pLen + 1]);
+ return new byte[][] {
+ unwrapped, // session key
+ new byte[] { enc[pLen + 1] }, // sym alg
+ new byte[0] // X25519, X448 does not include checksum
+ };
}
}