diff --git a/pgpainless-core/build.gradle b/pgpainless-core/build.gradle index 64d538d5..dd88b694 100644 --- a/pgpainless-core/build.gradle +++ b/pgpainless-core/build.gradle @@ -12,6 +12,9 @@ dependencies { testImplementation "org.junit.jupiter:junit-jupiter-params:$junitVersion" testRuntimeOnly "org.junit.jupiter:junit-jupiter-engine:$junitVersion" + // Jazzer for Fuzzing + testImplementation "com.code-intelligence:jazzer-junit:$jazzerVersion" + // Mocking Components testImplementation "org.mockito:mockito-core:$mockitoVersion" diff --git a/pgpainless-core/src/test/kotlin/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTest.kt b/pgpainless-core/src/test/kotlin/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTest.kt new file mode 100644 index 00000000..bc73617a --- /dev/null +++ b/pgpainless-core/src/test/kotlin/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTest.kt @@ -0,0 +1,48 @@ +// SPDX-FileCopyrightText: 2025 Paul Schaub +// +// SPDX-License-Identifier: Apache-2.0 + +package org.pgpainless.bouncycastle.fuzzing + +import com.code_intelligence.jazzer.api.FuzzedDataProvider +import com.code_intelligence.jazzer.junit.DictionaryFile +import com.code_intelligence.jazzer.junit.FuzzTest +import org.bouncycastle.bcpg.ArmoredInputException +import org.bouncycastle.bcpg.UnsupportedPacketVersionException +import org.bouncycastle.openpgp.PGPException +import org.bouncycastle.openpgp.PGPUtil +import org.bouncycastle.openpgp.bc.BcPGPObjectFactory +import java.io.EOFException +import java.io.IOException + +class PGPObjectFactoryFuzzingTest { + + @FuzzTest + @DictionaryFile(resourcePath = "ascii_armor.dict") + @DictionaryFile(resourcePath = "openpgp.dict") + fun parseFuzzedObjects(provider: FuzzedDataProvider) { + val encoding = provider.consumeRemainingAsBytes() + + if (encoding.isEmpty()) { + return + } + try { + val decIn = PGPUtil.getDecoderStream(encoding.inputStream()) + val objFac = BcPGPObjectFactory(decIn) + var obj = objFac.nextObject() + while (obj != null) { + obj = objFac.nextObject() + } + } catch (e: ArmoredInputException) { + return + } catch (e: PGPException) { + return + } catch (e: EOFException) { + return + } catch (e: IOException) { + return + } catch (e: UnsupportedPacketVersionException) { + return + } + } +} diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-109647c495d69596e778c9d8077547b9ecf227a3 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-109647c495d69596e778c9d8077547b9ecf227a3 new file mode 100644 index 00000000..da0c74a5 Binary files /dev/null and b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-109647c495d69596e778c9d8077547b9ecf227a3 differ diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-16f60c43a386bd079675f4b67da22a6abc986fa1 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-16f60c43a386bd079675f4b67da22a6abc986fa1 new file mode 100644 index 00000000..be665fdb --- /dev/null +++ b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-16f60c43a386bd079675f4b67da22a6abc986fa1 @@ -0,0 +1 @@ +---Q(K \ No newline at end of file diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-461a23344509ca1e8cae0c6e76d4dd3c8adf45d1 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-461a23344509ca1e8cae0c6e76d4dd3c8adf45d1 new file mode 100644 index 00000000..56ffc575 Binary files /dev/null and b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-461a23344509ca1e8cae0c6e76d4dd3c8adf45d1 differ diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-a2bb907275f07fc8b8522f3d1d88f7a77b7cb193 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-a2bb907275f07fc8b8522f3d1d88f7a77b7cb193 new file mode 100644 index 00000000..8993c6b6 Binary files /dev/null and b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-a2bb907275f07fc8b8522f3d1d88f7a77b7cb193 differ diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-b8484cb8aa998062e1c4a7e8f749287d29b1a3b8 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-b8484cb8aa998062e1c4a7e8f749287d29b1a3b8 new file mode 100644 index 00000000..5c5d37a4 Binary files /dev/null and b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-b8484cb8aa998062e1c4a7e8f749287d29b1a3b8 differ diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-f25481c9ffd17932404b3826bbc97c6a1f818446 b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-f25481c9ffd17932404b3826bbc97c6a1f818446 new file mode 100644 index 00000000..827e0ae5 --- /dev/null +++ b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/crash-f25481c9ffd17932404b3826bbc97c6a1f818446 @@ -0,0 +1,5 @@ +-----BEGIN PGP MESSAG----- + +ywtiAAECAwTA/+66vg== +=pAS2 +-----END PGP MESSAGE----- diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/literaldata_binary_coffeebabe.asc b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/literaldata_binary_coffeebabe.asc new file mode 100644 index 00000000..83e2bbfa --- /dev/null +++ b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/PGPObjectFactoryFuzzingTestInputs/parseFuzzedObjects/literaldata_binary_coffeebabe.asc @@ -0,0 +1,5 @@ +-----BEGIN PGP MESSAGE----- + +ywtiAAECAwTA/+66vg== +=pAS2 +-----END PGP MESSAGE----- diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/ascii_armor.dict b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/ascii_armor.dict new file mode 100644 index 00000000..210b06c5 --- /dev/null +++ b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/ascii_armor.dict @@ -0,0 +1,39 @@ +# +# AFL Dictionary for OpenPGP (RFC9580) +# ------------------------------------------ +# +# Created by Paul Schaub + +# +# ASCII Armor +# +BEGIN_PGP_MESSAGE="-----BEGIN PGP MESSAGE-----" +END_PGP_MESSAGE="-----END PGP MESSAGE-----" +BEGIN_PGP_SIGNATURE="-----BEGIN PGP SIGNATURE-----" +END_PGP_SIGNATURE="-----END PGP SIGNATURE-----" +BEGIN_PGP_PUBLIC_KEY="-----BEGIN PGP PUBLIC KEY-----" +END_PGP_PUBLIC_KEY="-----END PGP PUBLIC KEY-----" +BEGIN_PGP_PUBLIC_KEY_BLOCK="-----BEGIN PGP PUBLIC KEY BLOCK-----" +END_PGP_PUBLIC_KEY_BLOCK="-----END PGP PUBLIC KEY BLOCK-----" +BEGIN_PGP_PRIVATE_KEY="-----BEGIN PGP PRIVATE KEY-----" +END_PGP_PRIVATE_KEY="-----END PGP PRIVATE KEY-----" +BEGIN_PGP_PRIVATE_KEY_BLOCK="-----BEGIN PGP PRIVATE KEY BLOCK-----" +END_PGP_PRIVATE_KEY_BLOCK="-----END PGP PRIVATE KEY BLOCK-----" +BEGIN_PGP_SIGNED_MESSAGE="-----BEGIN PGP SIGNED MESSAGE-----" + +HEADER_VERSION="Version" +HEADER_COMMENT="Comment" +HEADER_HASH="Hash" +HEADER_CHARSET="Charset" +HASH_SHA224="SHA224" +HASH_SHA256="SHA256" +HASH_SHA384="SHA384" +HASH_SHA512="SHA512" + +PART_BEGIN="BEGIN" +PART_PGP="PGP" +PART_MESSAGE="MESSAGE" +PART_BLOCK="BLOCK" +PART_PUBLIC="PUBLIC" +PART_PRIVATE="PRIVATE" +PART_KEY="KEY" diff --git a/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/openpgp.dict b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/openpgp.dict new file mode 100644 index 00000000..e0eb30dc --- /dev/null +++ b/pgpainless-core/src/test/resources/org/pgpainless/bouncycastle/fuzzing/openpgp.dict @@ -0,0 +1,34 @@ +# +# AFL Dictionary for OpenPGP (RFC9580) +# ------------------------------------------ +# +# Created by Paul Schaub + +# +# Packet Type IDs +# +RESERVED="\x00" +PKESK="\x01" +SIG="\x02" +SKESK="\0x03" +OPS="\x04" +SECKEY="\x05" +PUBKEY="\x06" +SECSUBKEY="\x07" +COMP="\x08" +SED="\x09" +MARKER="\x0A" +LIT="\x0B" +TRUST="\x0C" +UID="\x0D" +PUBSUBKEY="\x0E" +UAT="\x11" +SEIPD="\x12" +MOD="\x13" +RES20="\x14" +PADDING="\x15" + +# +# Entire Packets +# +MARKER_PACKET="\xCA\x03\x50\x47\x50" \ No newline at end of file