From 6356c79990c7e4ea920dab39c060265d85b18a60 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 5 Aug 2024 09:29:38 +0200 Subject: [PATCH] WIP --- .../bouncycastle/bcpg/AEADEncDataPacket.java | 4 ++ .../openpgp/PGPPublicKeyEncryptedData.java | 65 ++++++++++++------ .../bc/BcPublicKeyDataDecryptorFactory.java | 67 ++++++++++--------- 3 files changed, 85 insertions(+), 51 deletions(-) diff --git a/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java b/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java index 57cbf24293..31c41b9b47 100644 --- a/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java +++ b/pg/src/main/java/org/bouncycastle/bcpg/AEADEncDataPacket.java @@ -64,6 +64,10 @@ public byte getVersion() return version; } + /** + * Return the algorithm-id of the symmetric encryption algorithm used to encrypt the data. + * @return symmetric encryption algorithm + */ public byte getAlgorithm() { return algorithm; diff --git a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java index e18b5e60ff..4657410ccb 100644 --- a/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java +++ b/pg/src/main/java/org/bouncycastle/openpgp/PGPPublicKeyEncryptedData.java @@ -35,6 +35,26 @@ public class PGPPublicKeyEncryptedData this.keyData = keyData; } + private boolean confirmCheckSum( + byte[][] sessionInfo) + { + byte[] sessionKey = sessionInfo[0]; + byte[] checksum = sessionInfo[2]; + if (checksum.length == 0) + { + // has no checksum (e.g. X25519, X448) + return true; + } + + int check = 0; + for (int i = 0; i < sessionKey.length; i++) + { + check += sessionKey[i] & 0xff; + } + return checksum[0] == (byte) (check >> 8) && + checksum[1] == (byte) (check); + } + private boolean confirmCheckSum( byte[] sessionInfo) { @@ -111,42 +131,45 @@ public PGPSessionKey getSessionKey( PublicKeyDataDecryptorFactory dataDecryptorFactory) throws PGPException { - if (encData instanceof AEADEncDataPacket) + byte[][] sessionInfo = dataDecryptorFactory.recoverSessionKey(keyData, encData); + if (!confirmCheckSum(sessionInfo)) { - return dataDecryptorFactory.recoverSessionKey(keyData, (AEADEncDataPacket) encData); + throw new PGPException("Key checksum failed."); } - if (encData instanceof SymmetricEncDataPacket) + byte[] sessionKey = sessionInfo[0]; + int algorithm; + + // OCB (LibrePGP v5 style AEAD) + if (encData instanceof AEADEncDataPacket) { - return dataDecryptorFactory.recoverSessionKey(keyData, (SymmetricEncDataPacket) encData); + algorithm = ((AEADEncDataPacket) encData).getAlgorithm(); } - return dataDecryptorFactory.recoverSessionKey(keyData, (SymmetricEncIntegrityPacket) encData); - - byte[] sessionData = dataDecryptorFactory.recoverSessionData(keyData.getAlgorithm(), keyData.getEncSessionKey(), getVersion()); - if (keyData.getAlgorithm() == PublicKeyAlgorithmTags.X25519 || keyData.getAlgorithm() == PublicKeyAlgorithmTags.X448) + // SEIPD (OpenPGP v4 / OpenPGP v6) + else if (encData instanceof SymmetricEncIntegrityPacket) { - // X25519, X448 do no include checksum at the end of the session key - - int cipherAlg; - byte[] sessionKey; - if (keyData.getVersion() == PublicKeyEncSessionPacket.VERSION_3) + SymmetricEncIntegrityPacket seipd = (SymmetricEncIntegrityPacket) encData; + if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_1) { - cipherAlg = sessionData[0] & 0xff; - sessionKey = Arrays.copyOfRange(sessionData, 1, sessionData.length); + algorithm = sessionInfo[1][0]; + } + else if (seipd.getVersion() == SymmetricEncIntegrityPacket.VERSION_2) + { + algorithm = seipd.getCipherAlgorithm(); } else { - cipherAlg = ((SymmetricEncIntegrityPacket) encData).getCipherAlgorithm(); - sessionKey = sessionData; + throw new UnsupportedPacketVersionException("Unsupported SEIPD packet version: " + seipd.getVersion()); } - return new PGPSessionKey(cipherAlg, sessionKey); } - if (!confirmCheckSum(sessionData)) + // SED (Legacy, no integrity protection!) + else { - throw new PGPKeyValidationException("key checksum failed"); + algorithm = sessionInfo[1][0]; } - return new PGPSessionKey(sessionData[0] & 0xff, Arrays.copyOfRange(sessionData, 1, sessionData.length - 2)); + + return new PGPSessionKey(algorithm & 0xff, sessionKey); } /** 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 d779787c17..9ee946ae2c 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 @@ -67,6 +67,17 @@ public byte[][] recoverSessionKey(PublicKeyEncSessionPacket pkesk, InputStreamPa { case PublicKeyAlgorithmTags.X25519: + return recoverXSessionData(secKeyData[0], privKey, X25519PublicBCPGKey.LENGTH, + HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_128, new X25519Agreement(), + "X25519", new PublicKeyParametersOperation() + { + @Override + public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) + { + return new X25519PublicKeyParameters(pEnc, 0); + } + }, pkesk.getVersion()); + case PublicKeyAlgorithmTags.X448: return recoverXSessionData(secKeyData[0], privKey, X448PublicBCPGKey.LENGTH, HashAlgorithmTags.SHA512, SymmetricKeyAlgorithmTags.AES_256, new X448Agreement(), @@ -129,7 +140,12 @@ public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) rfc6637KDFCalculator = new RFC6637KDFCalculator(new BcPGPDigestCalculatorProvider().get(hashAlgorithm), symmetricKeyAlgorithm); KeyParameter key = new KeyParameter(rfc6637KDFCalculator.createKey(secret, userKeyingMaterial)); - return PGPPad.unpadSessionData(unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key)); + byte[] decrypted = PGPPad.unpadSessionData(unwrapSessionData(keyEnc, symmetricKeyAlgorithm, key)); + return new byte[][] { + Arrays.copyOfRange(decrypted, 0, decrypted.length - 3), + Arrays.copyOfRange(decrypted, decrypted.length - 3, decrypted.length - 2), + Arrays.copyOfRange(decrypted, decrypted.length - 2, decrypted.length), + }; } } catch (InvalidCipherTextException e) { throw new RuntimeException(e); @@ -143,44 +159,35 @@ public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) public byte[] recoverSessionData(int keyAlgorithm, byte[][] secKeyData, int pkeskVersion) throws PGPException { - if (keyAlgorithm != 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."); - } try { AsymmetricKeyParameter privKey = KEY_CONVERTER.getPrivateKey(pgpPrivKey); if (keyAlgorithm == PublicKeyAlgorithmTags.X25519) { - byte[] ephemeralKey = Arrays.copyOfRange(secKeyData[0], 0, X25519PublicBCPGKey.LENGTH); - int skLen = secKeyData[0][X25519PublicBCPGKey.LENGTH]; - byte[] encSessionKey = containsSKAlg(pkeskVersion) ? - Arrays.copyOfRange(secKeyData[0], X25519PublicBCPGKey.LENGTH + 2, secKeyData[0].length - 1) : - Arrays.copyOfRange(secKeyData[0], X25519PublicBCPGKey.LENGTH + 1, secKeyData[0].length); - byte[] decSessionKey = recoverXSessionData2() - return recoverXSessionData(secKeyData[0], privKey, X25519PublicBCPGKey.LENGTH, HashAlgorithmTags.SHA256, - SymmetricKeyAlgorithmTags.AES_128, new X25519Agreement(), "X25519", new PublicKeyParametersOperation() - { - @Override - public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) + byte[][] decrypted = recoverXSessionData(secKeyData[0], privKey, X25519PublicBCPGKey.LENGTH, HashAlgorithmTags.SHA256, + SymmetricKeyAlgorithmTags.AES_128, new X25519Agreement(), "X25519", new PublicKeyParametersOperation() { - return new X25519PublicKeyParameters(pEnc, 0); - } - }, pkeskVersion); + @Override + public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) + { + return new X25519PublicKeyParameters(pEnc, 0); + } + }, pkeskVersion); + return Arrays.concatenate(decrypted[0], decrypted[1], decrypted[2]); } else if (keyAlgorithm == 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) + byte[][] decrypted = recoverXSessionData(secKeyData[0], privKey, X448PublicBCPGKey.LENGTH, HashAlgorithmTags.SHA512, + SymmetricKeyAlgorithmTags.AES_256, new X448Agreement(), "X448", new PublicKeyParametersOperation() { - return new X448PublicKeyParameters(pEnc, 0); - } - }, pkeskVersion); + @Override + public AsymmetricKeyParameter getPublicKeyParameters(byte[] pEnc, int pEncOff) + { + return new X448PublicKeyParameters(pEnc, 0); + } + }, pkeskVersion); + return Arrays.concatenate(decrypted[0], decrypted[1], decrypted[2]); } else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) { @@ -220,7 +227,7 @@ else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) ECDomainParameters ecParameters = ((ECPrivateKeyParameters)privKey).getParameters(); ECPublicKeyParameters ephPub = new ECPublicKeyParameters(ecParameters.getCurve().decodePoint(pEnc), - ecParameters); + ecParameters); ECDHBasicAgreement agreement = new ECDHBasicAgreement(); agreement.init(privKey); @@ -244,7 +251,7 @@ else if (keyAlgorithm == PublicKeyAlgorithmTags.ECDH) c1.init(false, privKey); if (keyAlgorithm == PublicKeyAlgorithmTags.RSA_ENCRYPT - || keyAlgorithm == PublicKeyAlgorithmTags.RSA_GENERAL) + || keyAlgorithm == PublicKeyAlgorithmTags.RSA_GENERAL) { byte[] bi = secKeyData[0];