Skip to content

Commit

Permalink
Fix session key decryption for X25519, X448
Browse files Browse the repository at this point in the history
  • Loading branch information
vanitasvitae committed Jul 24, 2024
1 parent 1d5cc03 commit d7f36ba
Show file tree
Hide file tree
Showing 8 changed files with 135 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ public String toString()
return "" + keyId;
}

// -DM
return Hex.toHexString(fingerprint).toUpperCase();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public int getSymmetricAlgorithm(
{
if (keyData.getVersion() == PublicKeyEncSessionPacket.VERSION_3)
{
byte[] plain = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey());
byte[] plain = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey(), keyData.getVersion());
// symmetric cipher algorithm is stored in first octet of session data
return plain[0];
}
Expand All @@ -110,7 +110,7 @@ public PGPSessionKey getSessionKey(
PublicKeyDataDecryptorFactory dataDecryptorFactory)
throws PGPException
{
byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey());
byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey(), getVersion());
if (keyData.getAlgorithm() == PublicKeyAlgorithmTags.X25519 || keyData.getAlgorithm() == PublicKeyAlgorithmTags.X448)
{
return new PGPSessionKey(sessionData[0] & 0xff, Arrays.copyOfRange(sessionData, 1, sessionData.length));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,8 @@ public Iterator<PGPPublicKey> getKeysWithSignaturesBy(long keyID)
}

@Override
public Iterator<PGPPublicKey> getKeysWithSignaturesBy(KeyIdentifier identifier) {
public Iterator<PGPPublicKey> getKeysWithSignaturesBy(KeyIdentifier identifier)
{
List<PGPPublicKey> keysWithSigs = new ArrayList<>();
for (PGPPublicKey k : keys)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ public Iterator<PGPPublicKey> getKeysWithSignaturesBy(long keyID)
}

@Override
public Iterator<PGPPublicKey> getKeysWithSignaturesBy(KeyIdentifier identifier) {
public Iterator<PGPPublicKey> getKeysWithSignaturesBy(KeyIdentifier identifier)
{
List<PGPPublicKey> keysWithSigs = new ArrayList<>();
for (PGPSecretKey k : keys)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@
public interface PublicKeyDataDecryptorFactory
extends PGPDataDecryptorFactory
{
byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException;
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ public BcPublicKeyDataDecryptorFactory(PGPPrivateKey pgpPrivKey)
}

@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException
{
try
Expand All @@ -72,7 +72,7 @@ public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff)
{
return new X25519PublicKeyParameters(pEnc, 0);
}
});
}, pkeskVersion);
}
else if (keyAlgorithm == PublicKeyAlgorithmTags.X448)
{
Expand All @@ -84,7 +84,7 @@ public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff)
{
return new X448PublicKeyParameters(pEnc, 0);
}
});
}, pkeskVersion);
}
else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
{
Expand Down Expand Up @@ -230,22 +230,61 @@ private interface PublicKeyParametersOperation
AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff);
}

/**
*
* @param enc encrypted session key data
* @param privKey private key
* @param pLen length of the public key in bytes
* @param hashAlgorithm hash algorithm
* @param symmetricKeyAlgorithm symmetric key algorithm
* @param agreement agreement
* @param algorithmName name of the PK algorithm
* @param pkp public key parameters operation
* @return
* @throws PGPException
* @throws InvalidCipherTextException
*/
private byte[] getSessionData(byte[] enc, AsymmetricKeyParameter privKey, int pLen, int hashAlgorithm, int symmetricKeyAlgorithm,
RawAgreement agreement, String algorithmName, PublicKeyParametersOperation pkp)
RawAgreement agreement, String algorithmName, PublicKeyParametersOperation pkp, int pkeskVersion)
throws PGPException, InvalidCipherTextException
{
// ephemeral public key
byte[] pEnc = new byte[pLen];
byte[] keyEnc;
System.arraycopy(enc, 0, pEnc, 0, pLen);
// length of the following fields
int keyLen = enc[pLen] & 0xff;
assertOutOfRange(pLen + 1 + keyLen, enc);
keyEnc = new byte[keyLen - 1];
System.arraycopy(enc, pLen + 2, keyEnc, 0, keyEnc.length);
// encrypted session key
if (pkeskVersion == 6)
{
keyEnc = new byte[keyLen];
// skip length octet
System.arraycopy(enc, pLen + 1, keyEnc, 0, keyEnc.length);
}
else
{
keyEnc = new byte[keyLen - 1];
// skip length octet + sym algo
System.arraycopy(enc, pLen + 2, keyEnc, 0, keyEnc.length);
}

// shared point
byte[] secret = BcUtil.getSecret(agreement, privKey, pkp.getPublicKeyParameters(pEnc, 0));
// HKDF output
KeyParameter key = new KeyParameter(RFC6637KDFCalculator.createKey(hashAlgorithm, symmetricKeyAlgorithm,
Arrays.concatenate(pEnc, pgpPrivKey.getPublicKeyPacket().getKey().getEncoded(), secret), "OpenPGP " + algorithmName));
// session key
byte[] unwrapped = unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key);

return Arrays.concatenate(new byte[]{enc[pLen + 1]}, unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key));
if (pkeskVersion == 6)
{
return unwrapped;
}
else
{
return Arrays.prepend(unwrapped, enc[pLen + 1]);
}
}

private static byte[] unwrapSessionData(byte[] keyEnc, int symmetricKeyAlgorithm, KeyParameter key)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public PublicKeyDataDecryptorFactory build(final PrivateKey privKey)
final int expectedPayLoadSize = getExpectedPayloadSize(privKey);

@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException
{
if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH || keyAlgorithm == PublicKeyAlgorithmTags.X25519 || keyAlgorithm == PublicKeyAlgorithmTags.X448)
Expand Down Expand Up @@ -175,7 +175,7 @@ public PublicKeyDataDecryptorFactory build(final PGPPrivateKey privKey)
return new PublicKeyDataDecryptorFactory()
{
@Override
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData)
public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion)
throws PGPException
{
if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,23 @@
import org.bouncycastle.bcpg.PublicKeyEncSessionPacket;
import org.bouncycastle.bcpg.SymmetricEncIntegrityPacket;
import org.bouncycastle.bcpg.SymmetricKeyAlgorithmTags;
import org.bouncycastle.openpgp.PGPEncryptedDataList;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.PGPLiteralData;
import org.bouncycastle.openpgp.PGPObjectFactory;
import org.bouncycastle.openpgp.PGPPrivateKey;
import org.bouncycastle.openpgp.PGPPublicKeyEncryptedData;
import org.bouncycastle.openpgp.PGPSecretKey;
import org.bouncycastle.openpgp.PGPSecretKeyRing;
import org.bouncycastle.openpgp.bc.BcPGPObjectFactory;
import org.bouncycastle.openpgp.operator.PublicKeyDataDecryptorFactory;
import org.bouncycastle.openpgp.operator.bc.BcPublicKeyDataDecryptorFactory;
import org.bouncycastle.util.Arrays;
import org.bouncycastle.util.encoders.Hex;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;

public class EncryptedMessagePacketTest
Expand All @@ -27,7 +40,72 @@ public String getName()
public void performTest()
throws Exception
{
testPKESK6SEIPD2();
// testPKESK6SEIPD2FromTestVector();
// testPKESK6SEIPD2();
}

private void testPKESK6SEIPD2FromTestVector()
throws IOException, PGPException
{
String key = "-----BEGIN PGP PRIVATE KEY BLOCK-----\n" +
"\n" +
"xUsGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laMAGXKB\n" +
"exK+cH6NX1hs5hNhIB00TrJmosgv3mg1ditlsLfCsQYfGwoAAABCBYJjh3/jAwsJ\n" +
"BwUVCg4IDAIWAAKbAwIeCSIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" +
"2azJBScJAgcCAAAAAK0oIBA+LX0ifsDm185Ecds2v8lwgyU2kCcUmKfvBXbAf6rh\n" +
"RYWzuQOwEn7E/aLwIwRaLsdry0+VcallHhSu4RN6HWaEQsiPlR4zxP/TP7mhfVEe\n" +
"7XWPxtnMUMtf15OyA51YBMdLBmOHf+MZAAAAIIaTJINn+eUBXbki+PSAld2nhJh/\n" +
"LVmFsS+60WyvXkQ1AE1gCk95TUR3XFeibg/u/tVY6a//1q0NWC1X+yui3O24wpsG\n" +
"GBsKAAAALAWCY4d/4wKbDCIhBssYbE8GCaaX5NUt+mxyKwwfHifBilZwj2Ul7Ce6\n" +
"2azJAAAAAAQBIKbpGG2dWTX8j+VjFM21J0hqWlEg+bdiojWnKfA5AQpWUWtnNwDE\n" +
"M0g12vYxoWM8Y81W+bHBw805I8kWVkXU6vFOi+HWvv/ira7ofJu16NnoUkhclkUr\n" +
"k0mXubZvyl4GBg==\n" +
"-----END PGP PRIVATE KEY BLOCK-----\n";
byte[] pkesk = Hex.decode("c15d06210612c83f" +
"1e706f6308fe151a" +
"417743a1f033790e" +
"93e9978488d1db37" +
"8da99308851987cf" +
"18d5f1b53f817cce" +
"5a004cf393cc8958" +
"bddc065f25f84af5" +
"09b17dd3676418de" +
"a355437956617901" +
"e06957fbca8a6a47" +
"a5b5153e8d3ab7");
byte[] skesk = Hex.decode("d269020702066164" +
"16535be0b0716d60" +
"e052a56c4c407f9e" +
"b36b0efafe9ad0a0" +
"df9b033c69a21ba9" +
"ebd2c0ec95bf569d" +
"25c999ee4a3de170" +
"58f40dfa8b4c682b" +
"e3fbbbd7b27eb0f5" +
"9bb5005f80c7c6f4" +
"0388c30ad406ab05" +
"13dcd6f9fd737656" +
"286e1177d00f888a" +
"db31c4");

ByteArrayInputStream bIn = new ByteArrayInputStream(key.getBytes(StandardCharsets.UTF_8));
ArmoredInputStream aIn = new ArmoredInputStream(bIn);
BCPGInputStream pIn = new BCPGInputStream(aIn);
PGPObjectFactory objFac = new BcPGPObjectFactory(pIn);
PGPSecretKeyRing secretKeys = (PGPSecretKeyRing) objFac.nextObject();

bIn = new ByteArrayInputStream(Arrays.concatenate(pkesk, skesk));
pIn = new BCPGInputStream(bIn);
objFac = new BcPGPObjectFactory(pIn);
PGPEncryptedDataList encList = (PGPEncryptedDataList) objFac.nextObject();
PGPPublicKeyEncryptedData encData = (PGPPublicKeyEncryptedData) encList.get(0);
PGPSecretKey decKey = secretKeys.getSecretKey(encData.getKeyIdentifier());
PGPPrivateKey privKey = decKey.extractPrivateKey(null);
PublicKeyDataDecryptorFactory decryptor = new BcPublicKeyDataDecryptorFactory(privKey);
InputStream in = encData.getDataStream(decryptor);
objFac = new BcPGPObjectFactory(in);
PGPLiteralData literalData = (PGPLiteralData) objFac.nextObject();

}

private void testPKESK6SEIPD2()
Expand Down

0 comments on commit d7f36ba

Please sign in to comment.