Skip to content

Commit

Permalink
revert the wrong commit #1768 Implement message decryption using SEIP…
Browse files Browse the repository at this point in the history
…Dv2 and PKESKv6 packets
gefeili committed Sep 5, 2024
1 parent 1232b9f commit 6114940
Showing 11 changed files with 203 additions and 899 deletions.
Original file line number Diff line number Diff line change
@@ -60,23 +60,12 @@ else if (version == VERSION_6)
// anon recipient
keyVersion = 0;
keyFingerprint = new byte[0];
keyID = 0L;
}
else
{
keyVersion = in.read();
keyFingerprint = new byte[keyInfoLen - 1];
in.readFully(keyFingerprint);
// Derived key-ID from fingerprint
// TODO: Replace with getKeyIdentifier
if (keyVersion == PublicKeyPacket.VERSION_4)
{
keyID = FingerprintUtil.keyIdFromV4Fingerprint(keyFingerprint);
}
else
{
keyID = FingerprintUtil.keyIdFromV6Fingerprint(keyFingerprint);
}
}
}
else
Original file line number Diff line number Diff line change
@@ -72,13 +72,13 @@ public int getSymmetricAlgorithm(
{
if (keyData.getVersion() == PublicKeyEncSessionPacket.VERSION_3)
{
byte[] plain = dataDecryptorFactory.recoverSessionData(keyData, encData);
byte[] plain = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey());
// symmetric cipher algorithm is stored in first octet of session data
return plain[0];
}
else if (keyData.getVersion() == PublicKeyEncSessionPacket.VERSION_6)
{
// PKESK v6 stores the cipher algorithm in the SEIPD v2 packet fields.
// PKESK v5 stores the cipher algorithm in the SEIPD v2 packet fields.
return ((SymmetricEncIntegrityPacket)encData).getCipherAlgorithm();
}
else
@@ -98,57 +98,16 @@ public PGPSessionKey getSessionKey(
PublicKeyDataDecryptorFactory dataDecryptorFactory)
throws PGPException
{
byte[] sessionInfo = dataDecryptorFactory.recoverSessionData(keyData, encData);

// Confirm and discard checksum
if (containsChecksum(keyData.getAlgorithm()))
{
if (!confirmCheckSum(sessionInfo))
{
throw new PGPException("Key checksum failed.");
}
sessionInfo = Arrays.copyOf(sessionInfo, sessionInfo.length - 2);
}

byte[] sessionKey = Arrays.copyOfRange(sessionInfo, 1, sessionInfo.length);
int algorithm;

// OCB (LibrePGP v5 style AEAD)
if (encData instanceof AEADEncDataPacket)
byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey());
if (keyData.getAlgorithm() == PublicKeyAlgorithmTags.X25519 || keyData.getAlgorithm() == PublicKeyAlgorithmTags.X448)
{
algorithm = ((AEADEncDataPacket) encData).getAlgorithm();
}

// SEIPD (OpenPGP v4 / OpenPGP v6)
else if (encData instanceof SymmetricEncIntegrityPacket)
{
SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData;
if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1)
{
algorithm = sessionInfo[0];
}
else if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_2)
{
algorithm = seipd.getCipherAlgorithm();
}
else
{
throw new UnsupportedPacketVersionException("Unsupported SEIPD packet version: " + seipd.getVersion());
}
return new PGPSessionKey(sessionData[0] & 0xff, Arrays.copyOfRange(sessionData, 1, sessionData.length));
}
// SED (Legacy, no integrity protection!)
else
if (!confirmCheckSum(sessionData))
{
algorithm = sessionInfo[0];
throw new PGPKeyValidationException("key checksum failed");
}

return new PGPSessionKey(algorithm & 0xff, sessionKey);
}

private boolean containsChecksum(int algorithm)
{
return algorithm != PublicKeyAlgorithmTags.X25519 &&
algorithm != PublicKeyAlgorithmTags.X448;
return new PGPSessionKey(sessionData[0] & 0xff, Arrays.copyOfRange(sessionData, 1, sessionData.length - 2));
}

/**
@@ -210,38 +169,13 @@ private InputStream getDataStream(
}
else
{
boolean withIntegrityPacket = encData instanceof SymmetricEncIntegrityPacket;

if (encData instanceof SymmetricEncIntegrityPacket)
{
SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData;
// SEIPD v1 (OpenPGP v4)
if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1)
{
PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(true, sessionKey.getAlgorithm(), sessionKey.getKey());

BCPGInputStream encIn = encData.getInputStream();
PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(withIntegrityPacket, sessionKey.getAlgorithm(), sessionKey.getKey());

processSymmetricEncIntegrityPacketDataStream(true, dataDecryptor, encIn);
}
// SEIPD v2 (OpenPGP v6 AEAD)
else
{
PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(seipd, sessionKey);

BCPGInputStream encIn = encData.getInputStream();

encStream = new BCPGInputStream(dataDecryptor.getInputStream(encIn));
}
}
// SED (Symmetrically Encrypted Data without Integrity Protection; Deprecated)
else
{
PGPDataDecryptor dataDecryptor = dataDecryptorFactory.createDataDecryptor(false, sessionKey.getAlgorithm(), sessionKey.getKey());

BCPGInputStream encIn = encData.getInputStream();
BCPGInputStream encIn = encData.getInputStream();

processSymmetricEncIntegrityPacketDataStream(false, dataDecryptor, encIn);
}
processSymmetricEncIntegrityPacketDataStream(withIntegrityPacket, dataDecryptor, encIn);

//
// some versions of PGP appear to produce 0 for the extra

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,39 +1,10 @@
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 plain session info by decrypting the encrypted session key.
* The session info ALWAYS has the symmetric algorithm ID prefixed, so the return value is:
* <pre>[sym-alg][session-key][checksum]?</pre>
*
* @param pkesk public-key encrypted session-key packet
* @param encData encrypted data (sed/seipd/oed) packet
* @return decrypted session info
* @throws PGPException
*/
byte[] recoverSessionData(PublicKeyEncSessionPacket pkesk, InputStreamPacket encData)
byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
throws PGPException;

/**
* Recover the plain session info by decrypting the encrypted session key.
* This method returns the decrypted session info as-is (without prefixing missing cipher algorithm),
* so the return value is:
* <pre>[sym-alg]?[session-key][checksum]?</pre>
*
* @deprecated use {@link #recoverSessionData(PublicKeyEncSessionPacket, InputStreamPacket)} instead.
* @param keyAlgorithm public key algorithm
* @param secKeyData encrypted session key data
* @param pkeskVersion version of the PKESK packet
* @return decrypted session info
* @throws PGPException
*/
byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException;

}

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ static X9ECParameters getX9Parameters(ASN1ObjectIdentifier curveOID)
static HybridValueParameterSpec getHybridValueParameterSpecWithPrepend(byte[] ephmeralPublicKey, PublicKeyPacket pkp, String algorithmName)
throws IOException
{
return new HybridValueParameterSpec(Arrays.concatenate(ephmeralPublicKey, pkp.getKey().getEncoded()), true, new UserKeyingMaterialSpec(Strings.toByteArray("OpenPGP " + algorithmName)));
return new HybridValueParameterSpec(Arrays.concatenate(ephmeralPublicKey, pkp.getEncoded()), true, new UserKeyingMaterialSpec(Strings.toByteArray("OpenPGP " + algorithmName)));
}

static Key getSecret(OperatorHelper helper, PublicKey cryptoPublicKey, String keyEncryptionOID, String agreementName, AlgorithmParameterSpec ukmSpec, Key privKey)
Original file line number Diff line number Diff line change
@@ -39,7 +39,6 @@
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKey;
import org.bouncycastle.openpgp.PGPSessionKey;
import org.bouncycastle.openpgp.operator.AbstractPublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.PGPDataDecryptor;
import org.bouncycastle.openpgp.operator.PGPPad;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
@@ -128,12 +127,12 @@ else if (key instanceof RSAKey)

public PublicKeyDataDecryptorFactory build(final PrivateKey privKey)
{
return new AbstractPublicKeyDataDecryptorFactory()
return new PublicKeyDataDecryptorFactory()
{
final int expectedPayLoadSize = getExpectedPayloadSize(privKey);

@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
throws PGPException
{
if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH || keyAlgorithm == PublicKeyAlgorithmTags.X25519 || keyAlgorithm == PublicKeyAlgorithmTags.X448)
@@ -171,26 +170,25 @@ public PGPDataDecryptor createDataDecryptor(SymmetricEncIntegrityPacket seipd, P

public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey)
{
return new AbstractPublicKeyDataDecryptorFactory()
return new PublicKeyDataDecryptorFactory()
{
@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
throws PGPException
{
boolean containsSKAlg = containsSKAlg(pkeskVersion);
if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
{
return decryptSessionData(keyConverter, privKey, secKeyData);
}
else if (keyAlgorithm == PublicKeyAlgorithmTags.X25519)
{
return decryptSessionData(keyConverter, privKey, secKeyData[0], X25519PublicBCPGKey.LENGTH, "X25519withSHA256HKDF",
SymmetricKeyAlgorithmTags.AES_128, EdECObjectIdentifiers.id_X25519, "X25519", containsSKAlg);
SymmetricKeyAlgorithmTags.AES_128, EdECObjectIdentifiers.id_X25519, "X25519");
}
else if (keyAlgorithm == PublicKeyAlgorithmTags.X448)
{
return decryptSessionData(keyConverter, privKey, secKeyData[0], X448PublicBCPGKey.LENGTH, "X448withSHA512HKDF",
SymmetricKeyAlgorithmTags.AES_256, EdECObjectIdentifiers.id_X448, "X448", containsSKAlg);
SymmetricKeyAlgorithmTags.AES_256, EdECObjectIdentifiers.id_X448, "X448");
}
PrivateKey jcePrivKey = keyConverter.getPrivateKey(privKey);
int expectedPayLoadSize = getExpectedPayloadSize(jcePrivKey);
@@ -224,14 +222,6 @@ public PGPDataDecryptor createDataDecryptor(SymmetricEncIntegrityPacket seipd, P
};
}

/**
* Decrypt ECDH encrypted session keys.
* @param converter key converter
* @param privKey our private key
* @param secKeyData encrypted session key
* @return decrypted session key
* @throws PGPException
*/
private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey privKey, byte[][] secKeyData)
throws PGPException
{
@@ -243,12 +233,18 @@ private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey pr
byte[] keyEnc;

pLen = ((((enc[0] & 0xff) << 8) + (enc[1] & 0xff)) + 7) / 8;
checkRange(2 + pLen + 1, enc);
if ((2 + pLen + 1) > enc.length)
{
throw new PGPException("encoded length out of range");
}

pEnc = new byte[pLen];
System.arraycopy(enc, 2, pEnc, 0, pLen);
int keyLen = enc[pLen + 2] & 0xff;
checkRange(2 + pLen + 1 + keyLen, enc);
if ((2 + pLen + 1 + keyLen) > enc.length)
{
throw new PGPException("encoded length out of range");
}

keyEnc = new byte[keyLen];
System.arraycopy(enc, 2 + pLen + 1, keyEnc, 0, keyLen);
@@ -299,42 +295,26 @@ else if (ecKey.getCurveOID().equals(EdECObjectIdentifiers.id_X448))
}
}

/**
* Decrypt X25519 / X448 encrypted session keys.
* @param converter key converter
* @param privKey our private key
* @param enc encrypted session key
* @param pLen Key length
* @param agreementAlgorithm agreement algorithm
* @param symmetricKeyAlgorithm wrapping algorithm
* @param algorithmIdentifier ephemeral key algorithm identifier
* @param algorithmName public key algorithm name
* @param containsSKAlg whether the PKESK packet is version 3
* @return decrypted session data
* @throws PGPException
*/
private byte[] decryptSessionData(JcaPGPKeyConverter converter, PGPPrivateKey privKey, byte[] enc, int pLen, String agreementAlgorithm,
int symmetricKeyAlgorithm, ASN1ObjectIdentifier algorithmIdentifier, String algorithmName, boolean containsSKAlg)
int symmetricKeyAlgorithm, ASN1ObjectIdentifier algprithmIdentifier, String algorithmName)
throws PGPException
{
try
{
// ephemeral key (32 / 56 octets)
byte[] ephemeralKey = Arrays.copyOf(enc, pLen);

int size = enc[pLen] & 0xff;

checkRange(pLen + 1 + size, enc);

// encrypted session key
int sesKeyLen = size - (containsSKAlg ? 1 : 0);
int sesKeyOff = pLen + 1 + (containsSKAlg ? 1 : 0);
byte[] keyEnc = Arrays.copyOfRange(enc, sesKeyOff, sesKeyOff + sesKeyLen);

PublicKey ephemeralPubKey = getPublicKey(ephemeralKey, algorithmIdentifier, 0);
Key paddedSessionKey = getSessionKey(converter, privKey, agreementAlgorithm, ephemeralPubKey, symmetricKeyAlgorithm, keyEnc,
JcaJcePGPUtil.getHybridValueParameterSpecWithPrepend(ephemeralKey, privKey.getPublicKeyPacket(), algorithmName));
return paddedSessionKey.getEncoded();
byte[] pEnc = new byte[pLen];
System.arraycopy(enc, 0, pEnc, 0, pLen);
int keyLen = enc[pLen] & 0xff;
if ((pLen + 1 + keyLen) > enc.length)
{
throw new PGPException("encoded length out of range");
}
byte[] keyEnc = new byte[keyLen - 1];
System.arraycopy(enc, pLen + 2, keyEnc, 0, keyEnc.length);
PublicKey publicKey = getPublicKey(pEnc, algprithmIdentifier, 0);
Key paddedSessionKey = getSessionKey(converter, privKey, agreementAlgorithm, publicKey, symmetricKeyAlgorithm, keyEnc,
JcaJcePGPUtil.getHybridValueParameterSpecWithPrepend(pEnc, privKey.getPublicKeyPacket(), algorithmName));
symmetricKeyAlgorithm = enc[pLen + 1] & 0xff;
return Arrays.prepend(paddedSessionKey.getEncoded(), (byte)symmetricKeyAlgorithm);
}
catch (Exception e)
{
@@ -385,15 +365,6 @@ private void updateWithMPI(Cipher c, int expectedPayloadSize, byte[] encMPI)
}
}

/**
* Decrypt RSA / Elgamal encrypted session keys.
* @param keyAlgorithm public key algorithm
* @param privKey our private key
* @param expectedPayloadSize payload size
* @param secKeyData ESK data
* @return session data
* @throws PGPException
*/
private byte[] decryptSessionData(int keyAlgorithm, PrivateKey privKey, int expectedPayloadSize, byte[][] secKeyData)
throws PGPException
{
@@ -429,13 +400,4 @@ private byte[] decryptSessionData(int keyAlgorithm, PrivateKey privKey, int expe
throw new PGPException("exception decrypting session data", e);
}
}

private static void checkRange(int pLen, byte[] enc)
throws PGPException
{
if (pLen > enc.length)
{
throw new PGPException("encoded length out of range");
}
}
}
3 changes: 1 addition & 2 deletions pg/src/test/java/org/bouncycastle/bcpg/test/AllTests.java
Original file line number Diff line number Diff line change
@@ -23,8 +23,7 @@ public void testPacketParsing()
new SignaturePacketTest(),
new OnePassSignaturePacketTest(),
new OpenPgpMessageTest(),
new FingerprintUtilTest(),
new EncryptedMessagePacketTest()
new FingerprintUtilTest()
};

for (int i = 0; i != tests.length; i++)

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -74,8 +74,6 @@ public class RegressionTest
new LegacyX25519KeyPairTest(),
new LegacyX448KeyPairTest(),

new PGPv6MessageDecryptionTest(),

new Curve25519PrivateKeyEncodingTest(),
new EdDSAKeyConversionWithLeadingZeroTest(),
new ECDSAKeyPairTest()

0 comments on commit 6114940

Please sign in to comment.