From fef620d18b0588a31ca97648c4ffbfdfa1cde586 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Wed, 9 Jul 2025 23:45:25 +0200 Subject: [PATCH 1/5] Move jazzerVersion to version.gradle --- pgpainless-sop/build.gradle | 2 +- version.gradle | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/pgpainless-sop/build.gradle b/pgpainless-sop/build.gradle index adcbd283..70a4a743 100644 --- a/pgpainless-sop/build.gradle +++ b/pgpainless-sop/build.gradle @@ -19,7 +19,7 @@ dependencies { testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" // Jazzer for Fuzzing - testImplementation "com.code-intelligence:jazzer-junit:0.24.0" + testImplementation "com.code-intelligence:jazzer-junit:$jazzerVersion" // Logging testImplementation "ch.qos.logback:logback-classic:$logbackVersion" diff --git a/version.gradle b/version.gradle index c50556ac..2ba79714 100644 --- a/version.gradle +++ b/version.gradle @@ -10,6 +10,7 @@ allprojects { bouncyCastleVersion = '1.82-SNAPSHOT' bouncyPgVersion = bouncyCastleVersion junitVersion = '5.8.2' + jazzerVersion = '0.24.0' logbackVersion = '1.5.13' mockitoVersion = '4.5.1' slf4jVersion = '1.7.36' From 9df5060bab4f4eec7c99c6b6265e30047aeb28a0 Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Jul 2025 20:34:33 +0200 Subject: [PATCH 2/5] gitignore all .cifuzz-corpus directories --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 1ce9f38e..96a5578d 100644 --- a/.gitignore +++ b/.gitignore @@ -34,4 +34,4 @@ push_html.sh node_modules -pgpainless-sop/.cifuzz-corpus/* +*/.cifuzz-corpus/* From adf9fc4639592d6ee30d662058ae34b94e4d127b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Jul 2025 21:33:51 +0200 Subject: [PATCH 3/5] Fix IndexOutOfBounds, but keep decryption with only SK working --- .../decryption_verification/OpenPgpMessageInputStream.kt | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt index 5524dcae..9112273e 100644 --- a/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt +++ b/pgpainless-core/src/main/kotlin/org/pgpainless/decryption_verification/OpenPgpMessageInputStream.kt @@ -347,12 +347,7 @@ class OpenPgpMessageInputStream( "Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered.") syntaxVerifier.next(InputSymbol.ENCRYPTED_DATA) val encDataList = packetInputStream!!.readEncryptedDataList() - if (encDataList.isEmpty) { - LOGGER.debug( - "Missing encrypted session key packet.") - return false - } - if (!encDataList.isIntegrityProtected && !encDataList.get(0).isAEAD) { + if (!encDataList.isIntegrityProtected && !encDataList.isEmpty && !encDataList.get(0).isAEAD) { LOGGER.warn("Symmetrically Encrypted Data Packet is not integrity-protected.") if (!options.isIgnoreMDCErrors()) { throw MessageNotIntegrityProtectedException() From 5939b747b0d9959027f12d6fbf521e9633e27b6b Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Jul 2025 21:52:04 +0200 Subject: [PATCH 4/5] Replace consumeAsBytes(XXX) with consumeRemainingAsBytes() --- .../java/org/pgpainless/sop/fuzzing/AsciiArmorFuzzTest.java | 2 +- .../pgpainless/sop/fuzzing/EncryptedMessageFuzzingTest.java | 3 ++- .../java/org/pgpainless/sop/fuzzing/ParseCertFuzzTest.java | 2 +- .../org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java | 2 +- .../org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java | 2 +- .../java/org/pgpainless/sop/fuzzing/SignatureFuzzTest.java | 2 +- 6 files changed, 7 insertions(+), 6 deletions(-) diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/AsciiArmorFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/AsciiArmorFuzzTest.java index 7f068506..ead6098a 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/AsciiArmorFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/AsciiArmorFuzzTest.java @@ -22,7 +22,7 @@ public class AsciiArmorFuzzTest { maxDuration = "60s" ) public void armorAndDearmorData(FuzzedDataProvider data) throws IOException { - byte[] bytes = data.consumeBytes(1024); + byte[] bytes = data.consumeRemainingAsBytes(); byte[] armored = sop.armor().data(bytes).getBytes(); if (Arrays.areEqual(bytes, armored)) { diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/EncryptedMessageFuzzingTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/EncryptedMessageFuzzingTest.java index 4cc2daa0..5277cac2 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/EncryptedMessageFuzzingTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/EncryptedMessageFuzzingTest.java @@ -10,6 +10,7 @@ import org.bouncycastle.util.encoders.Hex; import org.bouncycastle.util.io.Streams; import org.junit.jupiter.api.BeforeAll; import org.junit.jupiter.api.Test; +import org.pgpainless.exception.MissingDecryptionMethodException; import org.pgpainless.exception.ModificationDetectionException; import org.pgpainless.sop.SOPImpl; import sop.SOP; @@ -75,7 +76,7 @@ public class EncryptedMessageFuzzingTest { maxDuration = "60s" ) public void decryptFuzzedMessage(FuzzedDataProvider provider) { - byte[] ciphertext = provider.consumeBytes(8192); + byte[] ciphertext = provider.consumeRemainingAsBytes(); if (ciphertext.length == 0) { return; } diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/ParseCertFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/ParseCertFuzzTest.java index 39296126..97324535 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/ParseCertFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/ParseCertFuzzTest.java @@ -22,7 +22,7 @@ public class ParseCertFuzzTest { @FuzzTest(maxDuration = "30s") public void parseOpenPGPCert(FuzzedDataProvider data) throws IOException { - byte[] certEncoding = data.consumeBytes(8192); + byte[] certEncoding = data.consumeRemainingAsBytes(); if (certEncoding.length == 0) { return; } diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java index bbd7e662..a5f6acc3 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java @@ -20,7 +20,7 @@ public class PublicKeyPacketFuzzTest { @FuzzTest(maxDuration = "30m") public void parsePublicKeyPacket(FuzzedDataProvider provider) { - byte[] encoding = provider.consumeBytes(8192); + byte[] encoding = provider.consumeRemainingAsBytes(); if (encoding.length == 0) { return; } diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java index 2d0e94b5..670050eb 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java @@ -20,7 +20,7 @@ public class SecretKeyPacketFuzzTest { @FuzzTest(maxDuration = "30m") public void parseSecretKeyPacket(FuzzedDataProvider provider) { - byte[] encoding = provider.consumeBytes(8192); + byte[] encoding = provider.consumeRemainingAsBytes(); if (encoding.length == 0) { return; } diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SignatureFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SignatureFuzzTest.java index 6d1be4a2..c13057b0 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SignatureFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SignatureFuzzTest.java @@ -273,7 +273,7 @@ public class SignatureFuzzTest { maxDuration = "60s" ) public void verifyFuzzedSig(FuzzedDataProvider provider) throws IOException { - byte[] sig = provider.consumeBytes(1024); + byte[] sig = provider.consumeRemainingAsBytes(); if (sig.length == 0) { return; } From 8f41fb0f2755b1faef19f83e7b92471328df9b3f Mon Sep 17 00:00:00 2001 From: Paul Schaub Date: Mon, 14 Jul 2025 22:09:51 +0200 Subject: [PATCH 5/5] key packet fuzzing tests: Use OpenPGPKey/OpenPGPCertificate API --- .../sop/fuzzing/PublicKeyPacketFuzzTest.java | 21 ++++++------------- .../sop/fuzzing/SecretKeyPacketFuzzTest.java | 18 +++++----------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java index a5f6acc3..ab961811 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/PublicKeyPacketFuzzTest.java @@ -6,33 +6,24 @@ package org.pgpainless.sop.fuzzing; import com.code_intelligence.jazzer.api.FuzzedDataProvider; import com.code_intelligence.jazzer.junit.FuzzTest; -import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.UnsupportedPacketVersionException; -import org.bouncycastle.openpgp.PGPObjectFactory; -import org.bouncycastle.openpgp.PGPPublicKeyRing; -import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.api.OpenPGPKeyReader; -import java.io.ByteArrayInputStream; import java.io.IOException; public class PublicKeyPacketFuzzTest { - @FuzzTest(maxDuration = "30m") - public void parsePublicKeyPacket(FuzzedDataProvider provider) - { + private final OpenPGPKeyReader reader = new OpenPGPKeyReader(); + + @FuzzTest(maxDuration = "60s") + public void parsePublicKeyPacket(FuzzedDataProvider provider) { byte[] encoding = provider.consumeRemainingAsBytes(); if (encoding.length == 0) { return; } - ByteArrayInputStream bIn = new ByteArrayInputStream(encoding); - BCPGInputStream pIn = new BCPGInputStream(bIn); - PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); try { - Object next = objFac.nextObject(); - if (next == null) return; - - PGPPublicKeyRing pubKey = (PGPPublicKeyRing) next; + reader.parseCertificate(encoding); } catch (IOException e) { // ignore } catch (UnsupportedPacketVersionException e) { diff --git a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java index 670050eb..cb3cfd9a 100644 --- a/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java +++ b/pgpainless-sop/src/test/java/org/pgpainless/sop/fuzzing/SecretKeyPacketFuzzTest.java @@ -6,18 +6,16 @@ package org.pgpainless.sop.fuzzing; import com.code_intelligence.jazzer.api.FuzzedDataProvider; import com.code_intelligence.jazzer.junit.FuzzTest; -import org.bouncycastle.bcpg.BCPGInputStream; import org.bouncycastle.bcpg.UnsupportedPacketVersionException; -import org.bouncycastle.openpgp.PGPObjectFactory; -import org.bouncycastle.openpgp.PGPSecretKeyRing; -import org.bouncycastle.openpgp.bc.BcPGPObjectFactory; +import org.bouncycastle.openpgp.api.OpenPGPKeyReader; -import java.io.ByteArrayInputStream; import java.io.IOException; public class SecretKeyPacketFuzzTest { - @FuzzTest(maxDuration = "30m") + private final OpenPGPKeyReader reader = new OpenPGPKeyReader(); + + @FuzzTest(maxDuration = "6ßs") public void parseSecretKeyPacket(FuzzedDataProvider provider) { byte[] encoding = provider.consumeRemainingAsBytes(); @@ -25,14 +23,8 @@ public class SecretKeyPacketFuzzTest { return; } - ByteArrayInputStream bIn = new ByteArrayInputStream(encoding); - BCPGInputStream pIn = new BCPGInputStream(bIn); - PGPObjectFactory objFac = new BcPGPObjectFactory(pIn); try { - Object next = objFac.nextObject(); - if (next == null) return; - - PGPSecretKeyRing secKey = (PGPSecretKeyRing) next; + reader.parseKey(encoding); } catch (IOException e) { // ignore } catch (UnsupportedPacketVersionException e) {