1
0
Fork 0
mirror of https://github.com/pgpainless/pgpainless.git synced 2025-09-09 10:19:39 +02:00

Compare commits

...

20 commits

Author SHA1 Message Date
7daa3783bc
Fuzz PGPObjectFactory 2025-07-23 11:27:02 +02:00
4d46edf3b6
Add fuzzer test for generate-key for testing fuzzed passphrases and user-ids 2025-07-23 11:27:01 +02:00
7656bcd101
key packet fuzzing tests: Use OpenPGPKey/OpenPGPCertificate API 2025-07-23 11:27:01 +02:00
9f8ce475eb
Replace consumeAsBytes(XXX) with consumeRemainingAsBytes() 2025-07-23 11:27:01 +02:00
4f8c52d215
Fix IndexOutOfBounds, but keep decryption with only SK working 2025-07-23 11:27:01 +02:00
16a2bf27eb
gitignore all .cifuzz-corpus directories 2025-07-23 11:27:01 +02:00
742f8fa1a3
Move jazzerVersion to version.gradle 2025-07-23 11:27:00 +02:00
916809ad60
More fuzzing tests and vectors 2025-07-23 11:27:00 +02:00
002bd87136
Even more fuzzing 2025-07-23 11:27:00 +02:00
05cea3e5a9
More fuzzing 2025-07-23 11:27:00 +02:00
95eb73a8c7
Add more test vectors 2025-07-23 11:27:00 +02:00
06b3452c6c
Add fuzzing data to REUSE.toml 2025-07-23 11:27:00 +02:00
307b3ae40b
Fuzzer tests 2025-07-23 11:26:59 +02:00
0d8ce6a50b
Fuzz different methods 2025-07-23 11:26:59 +02:00
32dc1fa1a1
Add pgpainless-sop/.cifuzz-corpus/* to .gitignore 2025-07-23 11:26:59 +02:00
4a4f85767a
SOP armor: catch IOException during inspection of data 2025-07-23 11:26:59 +02:00
d1826eb961
OpenPGPInputStream: Expose inspectBuffer method 2025-07-23 11:26:59 +02:00
0d807cb6b8
Fix typo in error message 2025-07-23 11:26:40 +02:00
9b0a3cd4c7
Do not trim passphrases automatically 2025-07-23 11:26:28 +02:00
0ee31b232a
Allow UserIDs with trailing/leading whitespace and escape newlines in ASCII armor 2025-07-23 11:26:17 +02:00
165 changed files with 1532 additions and 55 deletions

2
.gitignore vendored
View file

@ -33,3 +33,5 @@ pgpainless-core/.settings/
push_html.sh
node_modules
*/.cifuzz-corpus/*

View file

@ -111,6 +111,12 @@ precedence = "aggregate"
SPDX-FileCopyrightText = "2022 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "Apache-2.0"
[[annotations]]
path = "pgpainless-sop/src/test/resources/org/pgpainless/sop/fuzzing/**"
precedence = "aggregate"
SPDX-FileCopyrightText = "2025 Paul Schaub <info@pgpainless.org>"
SPDX-License-Identifier = "CC0-1.0"
[[annotations]]
path = ".github/ISSUE_TEMPLATE/**"
precedence = "aggregate"

View file

@ -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"

View file

@ -81,7 +81,7 @@ public class OpenPgpInputStream extends BufferedInputStream {
this(in, true);
}
private void inspectBuffer() throws IOException {
public void inspectBuffer() throws IOException {
if (checkForAsciiArmor()) {
return;
}

View file

@ -139,7 +139,11 @@ class OpenPgpMessageInputStream(
// Comsume packets, potentially stepping into nested layers
layer@ while (run {
packet = pIn.nextPacketTag()
packet = try {
pIn.nextPacketTag()
} catch (e: NoSuchElementException) {
throw MalformedOpenPgpMessageException(e.message)
}
packet
} != null) {
@ -206,12 +210,25 @@ class OpenPgpMessageInputStream(
syntaxVerifier.next(InputSymbol.LITERAL_DATA)
val literalData = packetInputStream!!.readLiteralData()
val streamEncoding = try {
StreamEncoding.requireFromCode(literalData.format)
} catch (e: NoSuchElementException) {
throw PGPException("Invalid stream encoding format encountered: ${literalData.format}; ${e.message}")
}
val fileName = try {
literalData.fileName
} catch (e: IllegalArgumentException) {
// Non UTF8
throw PGPException("Cannot decode literal data filename: ${e.message}")
}
// Extract Metadata
layerMetadata.child =
LiteralData(
literalData.fileName,
fileName,
literalData.modificationTime,
StreamEncoding.requireFromCode(literalData.format))
streamEncoding)
nestedInputStream = literalData.inputStream
}
@ -221,10 +238,16 @@ class OpenPgpMessageInputStream(
signatures.enterNesting()
val compressedData = packetInputStream!!.readCompressedData()
val compAlg = try {
CompressionAlgorithm.requireFromId(compressedData.algorithm)
} catch (e: NoSuchElementException) {
throw PGPException(e.message)
}
// Extract Metadata
val compressionLayer =
CompressedData(
CompressionAlgorithm.requireFromId(compressedData.algorithm),
compAlg,
layerMetadata.depth + 1)
LOGGER.debug(
@ -324,7 +347,7 @@ class OpenPgpMessageInputStream(
"Symmetrically Encrypted Data Packet at depth ${layerMetadata.depth} encountered.")
syntaxVerifier.next(InputSymbol.ENCRYPTED_DATA)
val encDataList = packetInputStream!!.readEncryptedDataList()
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()
@ -546,7 +569,13 @@ class OpenPgpMessageInputStream(
pkesk: PGPPublicKeyEncryptedData
): Boolean {
try {
val decrypted = pkesk.getDataStream(decryptorFactory)
val decrypted = try {
pkesk.getDataStream(decryptorFactory)
} catch (e: ClassCastException) {
throw PGPException(e.message)
} catch (e: IllegalArgumentException) {
throw PGPException(e.message)
}
val sessionKey = SessionKey(pkesk.getSessionKey(decryptorFactory))
throwIfUnacceptable(sessionKey.algorithm)

View file

@ -17,6 +17,7 @@ import org.bouncycastle.openpgp.PGPOnePassSignature
import org.bouncycastle.openpgp.PGPPadding
import org.bouncycastle.openpgp.PGPSignature
import org.pgpainless.algorithm.OpenPgpPacket
import org.pgpainless.exception.MalformedOpenPgpMessageException
/**
* Since we need to update signatures with data from the underlying stream, this class is used to
@ -61,7 +62,12 @@ class TeeBCPGInputStream(inputStream: BCPGInputStream, outputStream: OutputStrea
fun readEncryptedDataList(): PGPEncryptedDataList {
delayedTee.squeeze()
return PGPEncryptedDataList(packetInputStream)
return try {
PGPEncryptedDataList(packetInputStream)
} catch (e: IllegalArgumentException) {
// Mismatched SKESK / SEIPD version
throw MalformedOpenPgpMessageException(e.message)
}
}
fun readOnePassSignature(): PGPOnePassSignature {

View file

@ -45,7 +45,7 @@ class KeyRingBuilder : KeyRingBuilderInterface<KeyRingBuilder> {
}
override fun addUserId(userId: CharSequence): KeyRingBuilder = apply {
userIds[userId.toString().trim()] = null
userIds[userId.toString()] = null
}
override fun addUserId(userId: ByteArray): KeyRingBuilder =

View file

@ -478,7 +478,7 @@ class SecretKeyRingEditor(
val prevBinding =
inspectKeyRing(secretKeyRing).getCurrentSubkeyBindingSignature(keyId)
?: throw NoSuchElementException(
"Previous subkey binding signaure for ${keyId.openPgpKeyId()} MUST NOT be null.")
"Previous subkey binding signature for ${keyId.openPgpKeyId()} MUST NOT be null.")
val bindingSig = reissueSubkeyBindingSignature(subkey, expiration, protector, prevBinding)
secretKeyRing = injectCertification(secretKeyRing, subkey, bindingSig)
}
@ -569,9 +569,10 @@ class SecretKeyRingEditor(
}
private fun sanitizeUserId(userId: CharSequence): CharSequence =
// TODO: Further research how to sanitize user IDs.
// e.g. what about newlines?
userId.toString().trim()
// I'm not sure, what kind of sanitization is needed.
// Newlines are allowed, they just need to be escaped when emitted in an ASCII armor header
// Trailing/Leading whitespace is also fine.
userId.toString()
private fun callbackFromRevocationAttributes(attributes: RevocationAttributes?) =
object : RevocationSignatureSubpackets.Callback {

View file

@ -8,6 +8,7 @@ import java.io.IOException
import java.io.InputStream
import java.util.*
import openpgp.plusSeconds
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
import org.bouncycastle.bcpg.sig.KeyExpirationTime
import org.bouncycastle.openpgp.*
import org.bouncycastle.util.encoders.Hex
@ -151,6 +152,7 @@ class SignatureUtils {
*/
@JvmStatic
fun readSignatures(inputStream: InputStream, maxIterations: Int): List<PGPSignature> {
try {
val signatures = mutableListOf<PGPSignature>()
val pgpIn = ArmorUtils.getDecoderStream(inputStream)
val objectFactory = ImplementationFactory.getInstance().getPGPObjectFactory(pgpIn)
@ -168,7 +170,8 @@ class SignatureUtils {
// getInputStream() does not do decompression, contrary to getDataStream().
Streams.drain(
(nextObject as PGPCompressedData)
.inputStream) // Skip packet without decompressing
.inputStream
) // Skip packet without decompressing
}
if (nextObject is PGPSignatureList) {
@ -182,6 +185,13 @@ class SignatureUtils {
pgpIn.close()
return signatures.toList()
} catch (e: UnsupportedPacketVersionException) {
throw PGPException("Unsupported packet version encountered.", e)
} catch (e: ClassCastException) {
throw PGPException("Unexpected packet encountered.", e)
} catch (e: IllegalArgumentException) {
throw PGPException("Malformed packet encountered.", e)
}
}
/**

View file

@ -247,7 +247,8 @@ class ArmorUtils {
.add(OpenPgpFingerprint.of(publicKey).prettyPrint())
// Primary / First User ID
(primary ?: first)?.let {
headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }.add(it)
headerMap.getOrPut(HEADER_COMMENT) { mutableSetOf() }
.add(it.replace("\n", "\\n").replace("\r", "\\r"))
}
// X-1 further identities
when (userIds.size) {

View file

@ -11,14 +11,9 @@ import org.bouncycastle.util.Arrays
*
* @param chars may be null for empty passwords.
*/
class Passphrase(chars: CharArray?) {
class Passphrase(private val chars: CharArray?) {
private val lock = Any()
private var valid = true
private val chars: CharArray?
init {
this.chars = trimWhitespace(chars)
}
/**
* Return a copy of the underlying char array. A return value of null represents an empty
@ -67,6 +62,13 @@ class Passphrase(chars: CharArray?) {
override fun hashCode(): Int = getChars()?.let { String(it) }.hashCode()
/**
* Return a copy of this [Passphrase], but with whitespace characters trimmed off.
*
* @return copy with trimmed whitespace
*/
fun withTrimmedWhitespace(): Passphrase = Passphrase(trimWhitespace(chars))
companion object {
/**

View file

@ -0,0 +1,48 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// 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
}
}
}

View file

@ -0,0 +1,5 @@
-----BEGIN PGP MESSAGÚ-----
ywtiAAECAwTA/+66vg==
=pAS2
-----END PGP MESSAGE-----

View file

@ -0,0 +1,5 @@
-----BEGIN PGP MESSAGE-----
ywtiAAECAwTA/+66vg==
=pAS2
-----END PGP MESSAGE-----

View file

@ -0,0 +1,39 @@
#
# AFL Dictionary for OpenPGP (RFC9580)
# ------------------------------------------
#
# Created by Paul Schaub <info@pgpainless.org>
#
# 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"

View file

@ -0,0 +1,34 @@
#
# AFL Dictionary for OpenPGP (RFC9580)
# ------------------------------------------
#
# Created by Paul Schaub <info@pgpainless.org>
#
# 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"

View file

@ -18,6 +18,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"
// Logging
testImplementation "ch.qos.logback:logback-classic:$logbackVersion"

View file

@ -14,6 +14,7 @@ import org.pgpainless.util.ArmoredOutputStreamFactory
import sop.Ready
import sop.exception.SOPGPException
import sop.operation.Armor
import java.io.IOException
/** Implementation of the `armor` operation using PGPainless. */
class ArmorImpl : Armor {
@ -26,8 +27,14 @@ class ArmorImpl : Armor {
val bufferedOutputStream = BufferedOutputStream(outputStream)
// Determine the nature of the given data
val openPgpIn = OpenPgpInputStream(data)
openPgpIn.reset()
val openPgpIn = OpenPgpInputStream(data, false).apply {
try {
inspectBuffer()
} catch (e: IOException) {
// ignore
}
reset()
}
if (openPgpIn.isAsciiArmored) {
// armoring already-armored data is an idempotent operation

View file

@ -4,6 +4,7 @@
package org.pgpainless.sop
import org.bouncycastle.bcpg.UnsupportedPacketVersionException
import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
@ -15,6 +16,7 @@ import org.pgpainless.algorithm.SymmetricKeyAlgorithm
import org.pgpainless.decryption_verification.ConsumerOptions
import org.pgpainless.exception.MalformedOpenPgpMessageException
import org.pgpainless.exception.MissingDecryptionMethodException
import org.pgpainless.exception.ModificationDetectionException
import org.pgpainless.exception.WrongPassphraseException
import org.pgpainless.util.Passphrase
import sop.DecryptionResult
@ -23,6 +25,8 @@ import sop.SessionKey
import sop.exception.SOPGPException
import sop.operation.Decrypt
import sop.util.UTF8Util
import java.util.zip.ZipException
import kotlin.NoSuchElementException
/** Implementation of the `decrypt` operation using PGPainless. */
class DecryptImpl : Decrypt {
@ -53,15 +57,32 @@ class DecryptImpl : Decrypt {
throw SOPGPException.BadData(e)
} catch (e: IOException) {
throw SOPGPException.BadData(e)
} finally {
} catch (e: UnsupportedPacketVersionException) {
throw SOPGPException.BadData(e)
} catch (e: ModificationDetectionException) {
throw SOPGPException.BadData(e)
}
finally {
// Forget passphrases after decryption
protector.clear()
}
return object : ReadyWithResult<DecryptionResult>() {
override fun writeTo(outputStream: OutputStream): DecryptionResult {
try {
Streams.pipeAll(decryptionStream, outputStream)
decryptionStream.close()
} catch (e: MalformedOpenPgpMessageException) {
throw SOPGPException.BadData(e)
} catch (e: ModificationDetectionException) {
throw SOPGPException.BadData(e)
} catch (e: ZipException) {
throw SOPGPException.BadData(e)
} catch (e: IOException) {
throw SOPGPException.BadData(e)
} catch (e: NoSuchElementException) {
throw SOPGPException.BadData(e)
}
val metadata = decryptionStream.metadata
if (!metadata.isEncrypted) {

View file

@ -29,8 +29,11 @@ class DetachedVerifyImpl : DetachedVerify {
override fun data(data: InputStream): List<Verification> {
try {
val verificationStream =
try {
PGPainless.decryptAndOrVerify().onInputStream(data).withOptions(options)
} catch (e: RuntimeException) {
throw SOPGPException.BadData(e)
}
Streams.drain(verificationStream)
verificationStream.close()

View file

@ -100,4 +100,14 @@ public class GenerateKeyTest {
assertThrows(SOPGPException.UnsupportedProfile.class, () ->
sop.generateKey().profile("invalid"));
}
@Test
public void generateKeyWithNewlinesInUserId() throws IOException {
byte[] keyBytes = sop.generateKey()
.userId("Foo\n\nBar")
.generate()
.getBytes();
assertTrue(new String(keyBytes).contains("Foo\\n\\nBar"));
}
}

View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.bouncycastle.util.Arrays;
import org.pgpainless.sop.SOPImpl;
import sop.SOP;
import java.io.IOException;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
public class AsciiArmorFuzzTest {
private final SOP sop = new SOPImpl();
@FuzzTest(
maxDuration = "60s"
)
public void armorAndDearmorData(FuzzedDataProvider data) throws IOException {
byte[] bytes = data.consumeRemainingAsBytes();
byte[] armored = sop.armor().data(bytes).getBytes();
if (Arrays.areEqual(bytes, armored)) {
// armoring already armored data is idempotent
return;
}
byte[] dearmored = sop.dearmor().data(armored).getBytes();
assertArrayEquals(bytes, dearmored, new String(armored));
}
}

View file

@ -0,0 +1,103 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
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;
import sop.exception.SOPGPException;
import sop.operation.Decrypt;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.stream.Stream;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
public class EncryptedMessageFuzzingTest {
private final SOP sop = new SOPImpl();
private final String password = "sw0rdf1sh";
private final byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
private static List<byte[]> keys;
@BeforeAll
public static void setup() throws IOException {
keys = getKeys();
}
private static List<byte[]> getKeys() throws IOException {
List<byte[]> keys = new ArrayList<>();
String dir = "/org/pgpainless/sop/fuzzing/testKeys";
InputStream in = EncryptedMessageFuzzingTest.class.getResourceAsStream(dir);
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String file;
while ((file = reader.readLine()) != null) {
if (!file.endsWith(".key.asc")) {
continue;
}
try(InputStream fIn = EncryptedMessageFuzzingTest.class.getResourceAsStream(dir + "/" + file)) {
byte[] b = Streams.readAll(fIn);
keys.add(b);
}
}
reader.close();
return keys;
}
@FuzzTest(
maxDuration = "60s"
)
public void decryptFuzzedMessage(FuzzedDataProvider provider) {
byte[] ciphertext = provider.consumeRemainingAsBytes();
if (ciphertext.length == 0) {
return;
}
try {
Decrypt decrypt = sop.decrypt();
for (byte[] k : keys) {
decrypt.withKey(k);
}
byte[] decrypted = decrypt.withPassword(password)
.ciphertext(ciphertext)
.toByteArrayAndResult()
.getBytes();
assertArrayEquals(data, decrypted);
} catch (SOPGPException.BadData e) {
// expected
} catch (SOPGPException.CannotDecrypt e) {
// expected
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}

View file

@ -0,0 +1,67 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.bouncycastle.openpgp.PGPException;
import org.bouncycastle.openpgp.api.KeyPassphraseProvider;
import org.bouncycastle.openpgp.api.OpenPGPKey;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import org.bouncycastle.openpgp.api.exception.KeyPassphraseException;
import org.bouncycastle.util.encoders.Hex;
import org.junit.jupiter.api.Test;
import org.pgpainless.sop.SOPImpl;
import sop.SOP;
import sop.exception.SOPGPException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import static org.junit.jupiter.api.Assertions.assertNotNull;
public class GenerateKeyFuzzTest {
private final SOP sop = new SOPImpl();
@FuzzTest(maxDuration = "5m")
public void generateKeyWithFuzzedUserId(FuzzedDataProvider provider) throws IOException {
String userId = provider.consumeRemainingAsString();
try {
byte[] keyBytes = sop.generateKey()
.userId(userId)
.generate()
.getBytes();
OpenPGPKey key = new OpenPGPKeyReader().parseKey(keyBytes);
assertNotNull(key.getUserId(userId), "Cannot fetch user-id for '" + userId + "' (" + Hex.toHexString(userId.getBytes(StandardCharsets.UTF_8)) + ")\n" + new String(keyBytes));
} catch (IllegalArgumentException e) {
// expected.
}
}
@FuzzTest
public void generateKeyWithFuzzedPassphrase(FuzzedDataProvider provider) throws IOException, KeyPassphraseException {
byte[] passphrase = provider.consumeRemainingAsBytes();
try {
byte[] keyBytes = sop.generateKey()
.withKeyPassword(passphrase)
.generate()
.getBytes();
OpenPGPKey key = new OpenPGPKeyReader().parseKey(keyBytes);
OpenPGPKey.OpenPGPPrivateKey pk = key.getPrimarySecretKey().unlock(new String(passphrase).toCharArray());
assertNotNull(pk, "Got null result unlocking key that was generated with passphrase 0x'" + Hex.toHexString(passphrase) + "'");
}
catch (SOPGPException.PasswordNotHumanReadable e) {
// expected.
}
catch (PGPException e) {
throw new RuntimeException("Cannot unlock key that was generated with passphrase 0x'" + Hex.toHexString(passphrase) + "'", e);
}
}
}

View file

@ -0,0 +1,52 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.bouncycastle.bcpg.ArmoredInputException;
import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
import org.bouncycastle.openpgp.api.OpenPGPCertificate;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import org.pgpainless.sop.SOPImpl;
import sop.SOP;
import java.io.EOFException;
import java.io.IOException;
public class ParseCertFuzzTest {
private final SOP sop = new SOPImpl();
@FuzzTest(maxDuration = "30s")
public void parseOpenPGPCert(FuzzedDataProvider data) throws IOException {
byte[] certEncoding = data.consumeRemainingAsBytes();
if (certEncoding.length == 0) {
return;
}
try {
OpenPGPCertificate cert = new OpenPGPKeyReader().parseCertificate(certEncoding);
}
catch (ArmoredInputException e) {
// ignore
}
catch (EOFException e) {
// ignore
}
catch (IOException e) {
// ignore
}
catch (UnsupportedPacketVersionException e) {
// ignore
}
catch (ClassCastException e) {
}
catch (OutOfMemoryError e) {
}
}
}

View file

@ -0,0 +1,35 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import java.io.IOException;
public class PublicKeyPacketFuzzTest {
private final OpenPGPKeyReader reader = new OpenPGPKeyReader();
@FuzzTest(maxDuration = "60s")
public void parsePublicKeyPacket(FuzzedDataProvider provider) {
byte[] encoding = provider.consumeRemainingAsBytes();
if (encoding.length == 0) {
return;
}
try {
reader.parseCertificate(encoding);
} catch (IOException e) {
// ignore
} catch (UnsupportedPacketVersionException e) {
// ignore
} catch (ClassCastException e) {
// ignore
}
}
}

View file

@ -0,0 +1,36 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.bouncycastle.bcpg.UnsupportedPacketVersionException;
import org.bouncycastle.openpgp.api.OpenPGPKeyReader;
import java.io.IOException;
public class SecretKeyPacketFuzzTest {
private final OpenPGPKeyReader reader = new OpenPGPKeyReader();
@FuzzTest(maxDuration = "6ßs")
public void parseSecretKeyPacket(FuzzedDataProvider provider)
{
byte[] encoding = provider.consumeRemainingAsBytes();
if (encoding.length == 0) {
return;
}
try {
reader.parseKey(encoding);
} catch (IOException e) {
// ignore
} catch (UnsupportedPacketVersionException e) {
// ignore
} catch (ClassCastException e) {
// ignore
}
}
}

View file

@ -0,0 +1,306 @@
// SPDX-FileCopyrightText: 2025 Paul Schaub <vanitasvitae@fsfe.org>
//
// SPDX-License-Identifier: Apache-2.0
package org.pgpainless.sop.fuzzing;
import com.code_intelligence.jazzer.api.FuzzedDataProvider;
import com.code_intelligence.jazzer.junit.FuzzTest;
import org.pgpainless.sop.SOPImpl;
import sop.SOP;
import sop.Verification;
import sop.exception.SOPGPException;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
public class SignatureFuzzTest {
private final SOP sop = new SOPImpl();
private final byte[] data = "Hello, World!\n".getBytes(StandardCharsets.UTF_8);
private final String v4_ed25519 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: D7BC FF6B B105 40D9 87F9 CB6E 542D C9F6 FCAE AD63\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"mDMEaGzu+BYJKwYBBAHaRw8BAQdAlqjB241N44drAJvxa3wx0uRb5bxuVNXrCwPZ\n" +
"yf4Qg+O0HEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7ClQQTFgoARwWCaGzu\n" +
"+AkQVC3J9vyurWMWIQTXvP9rsQVA2Yf5y25ULcn2/K6tYwKeAQKbAQUWAgMBAAQL\n" +
"CQgHBRUKCQgLBYkJZgF/ApkBAADVIwEAi599IgoqQbvetYicOt9XobSQKH+4/tB/\n" +
"cmHgD7HkGu8A/jYoA0CaYuYNWw8ZYQ8QCUIAkXApm8fO9iyTx0QU1kQMuDMEaGzu\n" +
"+BYJKwYBBAHaRw8BAQdA48KEYPTPnV86ycWzAk82aHPF2Fke5cLFsQn7/laFT1DC\n" +
"wBUEGBYKAH0Fgmhs7vgCngECmwIFFgIDAQAECwkIBwUVCgkIC18gBBkWCgAGBYJo\n" +
"bO74AAoJECL1fW9vQUTUldQBALcsF4e23VatOfew9CXwEiL6P5LMh7E8n/yUVR+j\n" +
"NBr9AP0bxSZ1UbdRIrWpg/Itpl9h98gtpT9rdVs02n0+xs5GDAAKCRBULcn2/K6t\n" +
"Y4UQAQCG/xpMgjpVNNs3wzHeVB0OCRKXsWkqHQr6xnEDAObdpwD7BD10DpdQDnSa\n" +
"HP7CArFQIuA78aIXpaVidfWVMu1mEAm4OARobO74EgorBgEEAZdVAQUBAQdAXgno\n" +
"M3Qa9wevqtyAY5MzVz3y6KTYtnfrC/YXG1fc7Q4DAQgHwnUEGBYKAB0Fgmhs7vgC\n" +
"ngECmwwFFgIDAQAECwkIBwUVCgkICwAKCRBULcn2/K6tY7GuAP9Kf1Ec1GJmZ99U\n" +
"HsgiN60os+6adMLj4G2ASiIbNSDvKgD9F/VLFIb/eN7kJQp3E5C15x5pMMKEI/rj\n" +
"wdrKmYH3aAw=\n" +
"=4uX6\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
private static final String v4_rsa4096 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: C2C1 DB5E 3AE2 9711 62D9 7BFB 101D 531D 1C69 12A7\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xsFNBGhtEaoBEACrLPrTPIE2pmjojrYJEDHkRLVqGE1RQ5DOvaaQTYv/IkLPeqGM\n" +
"NQUHBowKXZQ5mnJn66qztSdape0j+7QPRlt1XFeNHmsPl2lJ+2IUrWnf3XIN+SIo\n" +
"JE45Rt8u1hOyokDbwC+MAM0dcC8GiuN+TUlXbpdqgV3MREpHNuWu4u+19lT4/RYl\n" +
"AxJt5/NyGqW+0MoSSN4ioUhbZdqqgugW95VJfBkMiTX+6/wPw/tpfHIulwbak1B0\n" +
"PAGGQZpU1+6j+RUbACukW3nAM9rMuHdlms1QWk8IiRZogid9msEHYF1GiOcHoGH9\n" +
"N4uF5q+7XsosAeBi0P8ogdlg9TDMZJEw+GuL2XCEZD2qaiYXp0M712SS8qgpKWGw\n" +
"g9kqjCGAH8WmU+txhecWyyNA6fYllQzOn3os32bw0DV1sP9EmAR+xsW4r58rR7+q\n" +
"e9UH2G8ISfF16asSwl9vyLeOteG9WbZS6VLRy05nKnD8hkbphsVXxpLf5vtIB/DH\n" +
"qKb59W3azKzVI67vzeTmQqsP/aqIbcDZcpVRDxbx80zqZzHlfAwtb1mIVaSfJlm4\n" +
"ASmh1K+KWVqmYLn10iUwNub9DQ5NUBkYIdEt2u9FXai5ZB08mdxQDR/fsNHLc5SJ\n" +
"r16AcISKonFPewaCpDoxb76LkyrLGF/kudxZbOj5k9E3MqRk3FAqNah7XwARAQAB\n" +
"zRxBbGljZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+wsGdBBMBCgBRBYJobRGsCRAQ\n" +
"HVMdHGkSpxahBMLB21464pcRYtl7+xAdUx0caRKnApsBBRUKCQgLBRYCAwEABAsJ\n" +
"CAcJJwkBCQIJAwgBAp4JBYkJZgGAApkBAABfBw//QmBNjhqq7f5NIsv+WQIPhZh+\n" +
"fI0eogQnK45Ni64n1teXOLg8YnaHBFMQNpJcgCWK0zgem7TKk2nDNchWHohrvJMX\n" +
"WrSSkjWE+r24iFCiEuXt69rfbu/Ya5bHXdy4cmGgjmbkLuLZwqLZ4hm+IWA5ndF8\n" +
"l2hL2kDl9LXlgiDt0V85+t7VBHRF4S5MoTyj4osuczKL2O7tBw0vDRdEQIicOcYD\n" +
"UTADVDpqJpeCb0qhIsG9Hcf6XeXpzWLZyNe32isXM19GxCP1w+jtto6jEuJXvotu\n" +
"F3Yt8h6P24iLKnAGr8EhwWG6w4oTYpKkkcbn82NfGZqFZV74mh3pE8fKAFBGPa4q\n" +
"q2iftW8be+QPTZcTA5g0ZSWECGGzKqia6+q6tjThn6MSzWpB3Orb3Kr4v6GQ6GUE\n" +
"NVw0NACU/WrIMKJJzarlxgcndPgg/xUHF7btEifgfhtf5t/dVUG16p0x+bqu5GFZ\n" +
"e9fskH6Km5eNsTvBVFWRKzaPqJFlf27VB4nPHNhl3SuiIAZ3nq23jyfTjAByH7nQ\n" +
"URYRRlucC2+XPOj3YRs6JLfjppPLQX4txIuzFV1gTwRFofzH+c9i6rUYCNXGnBWA\n" +
"WO0mKcCZ63X+tmeeo0aAcPP+4Ze1kh7rNIYMp8p9wNneGfNjBn/qniU3b7XHrKrg\n" +
"N3ljkagsOKpZdSzd+KPOwU0EaG0RrAEQAOa9Ak2nUI0cGRSoBedM0yDRkUbCrGSN\n" +
"TuepnVrnIDzr1JNc1avad9Zn2Hj7YV1CjHPKvVdI8jmQ/g6T+ceKUnB5qXq7n7Wb\n" +
"HQTvlNaz7/T/Om4GOXprWa5XZdRoj8BrI0nId/KCK14VZUO7VHVkBh+RojtJwpZi\n" +
"3m8Y7ODU9MuNgrF6LDvViCHW80uMkW3hlZdTLKQmkYKDMEpS3z+ynd3UYzu6Orwk\n" +
"ir8OFcamk0sapTh+vNDTjdxji1zD47Nq6H6QaSX5tRuz+gwxkoPkeShemBU+E1Hw\n" +
"N5rNLvVmD/hZwMqg95m2GghHLAol2NKvwfPFGyHXSZ/H0iyhyfmhPwd0keg/oV0k\n" +
"CFaPbdE8FePefgFl4A5Njxkdp8Mqxm9ONPLjK2kWwGYCQBZi9R8N6O/310IwCJqc\n" +
"jBht87MychHyTImgdF3MlSQiWbWTzgeBq12353n7ZsfCI8IrX4hmW/7uvwDynusU\n" +
"bC+CZTQG46O4xYhYNmg7livBeTq0ljChMKcwob/H8a0SrYFOL/85QK6ZsIh38R0B\n" +
"nHR4Mx9wE92jj+fFmbqUCXDUhu1KwnhgfCZLqD90qoRemws5Fqelgr+pkVgD2haI\n" +
"V2PL4O5uEJ6wgp7uQUliR5v7zwH/7MYBlJgLdlV4Jpz9d3MUfYoFUMZA1CjFIjz9\n" +
"TDpZ2tjJmITtABEBAAHCw5UEGAEKAkkFgmhtEa0JEK0galCgMdvCFqEEdEBU9q1n\n" +
"gJHTPZc3rSBqUKAx28ICmwLBXSAEGQEKAAYFgmhtEa0ACgkQrSBqUKAx28KFpg//\n" +
"X2qYs9ZACD/7hw7Oj+AANkgTm7Jon2EYMwcRfTq5ZKuZ6FGaY6x8pZMRR3tmsTjV\n" +
"U8dWPmb3mgF2dn7iNoAn/vNLEl7iKy+Dj053gu29+6vGcMbCxQT8RhDFsWhYJR86\n" +
"yfW4ze3b0hv5bwchpueq+s/vqTmqhsjsTDTCzbx46Hrj643xOIlckKnrPMcErnE1\n" +
"0ZgL5J9pjfyZ1dZjip30LRspyXHSE0h2AAvHKjAggraM+SmdtGHu3SCefXTtAlLl\n" +
"hE2u0ifoQAb4iQX2Ca5EjP6Qvjxk8Zez6M6Rg4v6YDhm+S7j+086bgF5gTmb9RTM\n" +
"K352kjTR8t8EYk8Mm02oTVa0dEOtP0L9mfIbBM9VH5YGxEiQhjDsh5/dQYo0tJwn\n" +
"Ipf0edeqYk2ESS70yhgX2zbSBNS7AmxTHWI0x0n7UnSLsFHO6pX2PeAfL65XkTcK\n" +
"n59wptC/wDV2IFomtNFfNTX3k5XsxWDer+eF1liyZF4EwaUc0cVWCPVY07xuL6EJ\n" +
"bCJ8euhv5zslo1bmfm+EGHnVWelPurR1OWp+tqjUTZX05+w+dcIzIwYo0xHkbJV0\n" +
"3HvcEuEyxl9EN/+WovXqdUMdHiXPXL7JslOXVA30T1EQ/q8ubeL+6E9TmiVsHTsv\n" +
"nhsqFtkLsp2JveQuoPiYmI/+s/lPcGVGXWIRZhzgcWgAABuSD/9kREGB2W4MsXq9\n" +
"0shRP7ae0KjWbORVPSwJMQcORYIDAizNk882O9mccJfpdpLKXeib8Am5FoUdqsES\n" +
"5sZbb65hOr9vToLenICZ7Fm8Ojqk1W4U2XiF3aGGmalqK17ebfYqQXMTLqawcvQn\n" +
"G1amJ4x3qmV6NUMRVynhzZWqV53oZ0M+jVaBZkSG0MPTYMaDfpB9TSkbiCyg40OE\n" +
"PxVBrlHuQOf/hwfWH09V06WvY7/ATa9Ofy1VqIJif/UJgCAT7hxPqCYeMtID5Nm5\n" +
"sREn3Qq+jizZ770CwjnDu4xKcDasjoJkiFf648GmYCCbgXDcL+lbm52m9XDVb34L\n" +
"PUMr8u2BSyXMODNIW+lPBhsbAwp0iq3MuQUa00zmnebmilqhV8wV6surzy29ABbJ\n" +
"2z+JJh/DmqEiW+5AimxbhfbORKV6Bw3A60A53osz5tnx5H0odEA4Wy669yV0lqW0\n" +
"bi/Jx8FCpoI+0yvd5DEjQwRyJhdzuW1R5kMc2mM92jN+IjtA9vAqqJwl6X+TL7yT\n" +
"rB8u6SXGfRS3Uss7WaTiQJ3SmXCOwL4i5M7ME7mRHLwuw2iYQZETfUg/Gu6YlAlT\n" +
"MDhWt96BR28TG+Ijpls+DUF+CcBGXLBlaRSH3/jpycRmaqgXrfqh9CxWCG66LBIZ\n" +
"QkqC79Zn0Zonu1DGGU4rrdc4Na4Kgs7BTQRobRGtARAAvsYZoHkaH7hSJTqgy199\n" +
"Q3NJRp2PiqlG48LpHdSUF79fOvwQv0VBZ+ILG+h+Q2VxzLvn/cKpqZrsbBAa0QPQ\n" +
"pnfxtrn7W+jg9Ba+Pp36ugnc2Jc8NMO265AZh4OEn8Sqaj1kJSu01Ft2oT/1v0BZ\n" +
"p/W4kXas/RQHu6s9zHDN4l8ndrxZbmExAEn/2yLX4lMQQZsBGt8Mc23JFFLF8gek\n" +
"dmnjOyMuWuYslZ3P/74nofxHLdNfXhyIeuLP8RuAsP6ScA0F38CZt1kuD2CAf35B\n" +
"cLhm4noRaWYLi6WcQ80H1DlDIb8w0bzwZoZob1E2hHiT8UEYbLtYb5ASkdoCxz/p\n" +
"//LBCon4sarthEmyvGiHswpDnw1LRBIsIUYOoNYb/IajRjPfztQ0wiY7r6wmAKo9\n" +
"bRB/QZv/K9v6/NEWEVw5w5drAPyFwl4fkdp5QqtbGseUI25dj+JTruIU0xmtl6mL\n" +
"teN4tY2Php++lFTN0rFdHa4TlKBwjUBJgiNQ+YAwBrivDd7a3tTdPbcq8et3ahw4\n" +
"5gc0koGLco/mCGUgIqt8MPLG0nV5YhFYztPcHrXSjvoQCG25sevEeOHMq5NZw4JJ\n" +
"uhv0iz/cxdZcCZWIauuMvblDehDP2RVqlozkB7wG4+G0mGJxmPgltufKsnyycZgc\n" +
"xLCdKH1BoVG9Ght/PuJsRdEAEQEAAcLBdgQYAQoAKgWCaG0RtwkQq5KiltUsy68W\n" +
"oQTuGgCTnm/OWBS9N6OrkqKW1SzLrwKbDAAApw0P/3/5H5MOcFrrSOs5lC4DsBdJ\n" +
"H4JPgkS/IqqyMqfVuVzHXER7RXrO8W5K3tIY+uWxLx9nG2v79KA4djlPGKdMvQ1b\n" +
"fXAlSZFE3vHcV7VmvqoH46Ogz3z2hFaJdvImO7qZOq2qmMTPXsaK6zTwehEGNGF7\n" +
"tfLVxPlIN57hXPoD6y8ZsvXdcPBhnKHsRpFikKWVQukjXkFY3en44UdUReHkF6tH\n" +
"TSJ+kl6kVyfHVFcpvhiIq9M0RO0lSNbIl2nDnMgi4ks94aju7gSIw+t2SQcTAbRI\n" +
"C87c1b1PlgDEo8KaegPhKXKo8tUfhBMoL9VtOI2XoyrolsDcNi/yyznpc7DpLFld\n" +
"YMmfp5VORU0NdciZPChCa5clLiFxJZxI68a1oNcki3n4NSmu6yuBevTDufPAcXK7\n" +
"gC47o9NRgY+7EZvnqAnVnBzJCEG9bWJLcCZrrxz1YzP/BrLsa/VRVu5JQT8jFI25\n" +
"VifYKW60I/Zc4ApHMj7PjjtAjxcM8h68citOrk7DJr3vpL/XazFyoC64jZNtcSY8\n" +
"uWjVpEs2qpI/Kpag+3PQQtnSky+MMWdmztLmJ7bnD/R63HFg/Qf2U5U2fmTllnuc\n" +
"lh2++avCu1X41FFPmQCI0AcQMNxzw3nhHEqd9hPsWvnjfo7crvM6ExHKWzLzdFCQ\n" +
"XgWruN+wC+b9XY+Rrn+5\n" +
"=ppAk\n" +
"-----END PGP PUBLIC KEY BLOCK-----";
private static final String v4_nistP256 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: 7A00 864E BC9C 503A 05C7 4687 338C BC25 2B16 66D4\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xlIEaG0TDxMIKoZIzj0DAQcCAwRixWErG7QLr7ACFxUGjnVHuy1fzaHArCjhXQdJ\n" +
"cFkly6lxXqpth8a6Ef7ww4xicoCBUkMWDcMDmArolc8pzK7ZzRxBbGljZSA8YWxp\n" +
"Y2VAcGdwYWlubGVzcy5vcmc+wp8EExMKAFEFgmhtEw8JEDOMvCUrFmbUFqEEegCG\n" +
"TrycUDoFx0aHM4y8JSsWZtQCmwEFFQoJCAsFFgIDAQAECwkIBwknCQEJAgkDCAEC\n" +
"ngkFiQlmAYACmQEAAAjYAP9/DDAzA0Ykon8ACI6atMQYYnbIU894956akr64mmo9\n" +
"FgEAke0o9t/zrH0z1LD7yr3qkW0j+NK2OEy1XIjADrp5wRLOUgRobRMPEwgqhkjO\n" +
"PQMBBwIDBAqrl9l6YvdrzOWXO4ZFakQCI22HGw4U806IrjGuYFBwdhnu+lGFVFsD\n" +
"CGMXY2ZhslsSNAJNDrxAP+xeAulDaDvCwBgEGBMKAIoFgmhtEw8JEGTDz6W23cHo\n" +
"FqEE4bpPaE2vfK5LIsVuZMPPpbbdwegCmwJfIAQZEwoABgWCaG0TDwAKCRBkw8+l\n" +
"tt3B6HvEAQDN/84YKO0rDGbVyZEuVrOxeHXTC/8DlhHYx9Am5TZu9wD+NJCIzwzU\n" +
"a4fPISDUJRH+vxAHsRSp4rOCod+tgXsq+7wAANrKAP9dyYnyMEfZcts3i/BZo25D\n" +
"pqIrfDeZ1ZwP7mMZQ9lmRgEAqKKGBSLnLViyng5Z7Q5BqzW01q7CkLXZZ5IXaNns\n" +
"0XTOVgRobRMPEggqhkjOPQMBBwIDBC9IY09+8DOpG9aOK2iY6isENu6TvDClVvug\n" +
"SBnnsUdS3fOhVYYfcniboqrV6+BmfpbwptdGyD0yBUwzfL62/QADAQgHwngEGBMK\n" +
"ACoFgmhtEw8JEMlGff4yehhJFqEEC15pityd3XNDfeS0yUZ9/jJ6GEkCmwwAAHt4\n" +
"AQDG3xESM+ryzFUb8c4ldcb5NQ32eg0L1nFgTQEBHNJFpQEAi5xO1ZM4wEOlzk3e\n" +
"SC3qFyx9K6WQBlpY7TNdgqX8aEY=\n" +
"=1lGG\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
private static final String v4_nistP384 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: C52D CD97 0953 D8A9 3136 509D 20B0 88C6 9BBF 089C\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xm8EaG0TIhMFK4EEACIDAwTn7aRgEpefzWD3a9g/h7BA6EOVHxcV93BKE2Zf53kT\n" +
"Yvs13YmY1wTDjsYbHTGTMmm46Q+QSphhiWTVyodykqinFnhmmt/R1L4Z3qQWVBWE\n" +
"JNyS+swSqPSPQnIEygjqJrPNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7C\n" +
"vwQTEwoAUQWCaG0TIgkQILCIxpu/CJwWoQTFLc2XCVPYqTE2UJ0gsIjGm78InAKb\n" +
"AQUVCgkICwUWAgMBAAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAApfYBgNg1\n" +
"Ni0jkkT1TncTslHERidv+teYXTNhtE1Qe4fK9LM/iTruMo3KI5vLQ0yThODrtAF5\n" +
"AWzr3j/vhAZ0keXBTFiKj+8L564uGZ/Ab2wQUYVNgyrJ4nIylQqjl5v3AoxaQa8s\n" +
"zm8EaG0TIhMFK4EEACIDAwT2Pz19+CkZouSUqvLtDGKdOfiRoAg2TmVEMkjanwyM\n" +
"pml7g4XVh0D0WNktvTltHvDepZU4JO5Dse+zC+KlORk6w6KABrC8dujPlO2miFDO\n" +
"ziqrZClDgkdeJG0F4I/QIbjCwFgEGBMKAKoFgmhtEyIJEBxvr0D4OkYGFqEEB1Ic\n" +
"cXRzT2WMi+p2HG+vQPg6RgYCmwJ/IAQZEwoABgWCaG0TIgAKCRAcb69A+DpGBgnx\n" +
"AYDLFfyzoaL02LRlOULvqicJjDuWII4HHQKlWU0bL1CPdVGLizQBPVIrqOE7ne4X\n" +
"ofQBf3S4CH2TGJdG6joVa5J22VLXhKFVD9MsO7X+QpLcpvpcdWdZpu8pSEIAQ1ck\n" +
"VwJEAgAAzNMBf34fez/WxNFg7autqSXqSRjeQH6EgJeommuCZg90530WnDRQrm53\n" +
"a4Ava+p4nW766gGAxzPG8wZ9YiNFWXP80rPaFnVDUvpAzVN6QulNgA9X++PFdnn8\n" +
"udhXqbn/tjtmhz4ZznMEaG0TIhIFK4EEACIDAwSyypi9tH1VKoyz+whB1u8Poi9n\n" +
"9G5bWBpQwlkqDMz5P7D0To7m2bnif6qjXfBo0e43NLst7XL9YTXKGiHssGWdvBjW\n" +
"nUjM9UXWIpuigAEX5Run5Bn/mryfrIL6ocJIrJkDAQkIwpgEGBMKACoFgmhtEyIJ\n" +
"EB4h59bAP7IKFqEERagGFBZG9V3Kbwc8HiHn1sA/sgoCmwwAAI91AX4hX/+DJLdN\n" +
"jhZXEiY9SX3jyD97VfvZMTokCB7AfPGZ1GIMtaaTgN/fjhlZcQuoQhsBfjUvF5Rr\n" +
"S0CgvzBn3v/m72uykFayWZ06cHufSrgkR7UygCjGlVPO5bp4ZoPhW7FUqg==\n" +
"=UtdE\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
private static final String v4_nistP521 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: E17E 59B5 7DAD 1868 AA48 053F C762 4862 050A 405F\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xpMEaG0TLBMFK4EEACMEIwQAFc+kLfLeUvejNlyhZHH6DF9QGvYD0SzJnTQyecDP\n" +
"GUk0kUFPx0i2UFh/bFNvfqA5DFG/6WSnWcasSNH1YZAXx8cAUXyISgGSiuqGUztw\n" +
"/sSkcTSZ0ZJwHEMNlk8BPDdSgPadvy54BxSR+8bxGj+rFmgdB45V/RrXMYNyYAEF\n" +
"m9HINCfNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CwCIEExMKAFEFgmht\n" +
"EywJEMdiSGIFCkBfFqEE4X5ZtX2tGGiqSAU/x2JIYgUKQF8CmwEFFQoJCAsFFgID\n" +
"AQAECwkIBwknCQEJAgkDCAECngkFiQlmAYACmQEAAMf/AgQPsBNL2LNlZLHr8REB\n" +
"TS1ykw0S1RfEHir2XuneunYVxQD+WIK+f9O3lGoA79ojEnbWY3vn5KYL3uIQGTrs\n" +
"XeJAEAIJAUCmZetIcQD3aixOURerS8gS5pn8jAbSESpzaSyW/EFOCRpMo+zTkZYk\n" +
"/wFfYayLgtRiEscppMUfyb1lwjiN4vePzpMEaG0TLBMFK4EEACMEIwQBc8xQHGwC\n" +
"oG04BAmED/5Ju4EkQJC0qTdCLzktWOyHoVplqcUuMR27cf+kHlF65VUJ2OsX8ETH\n" +
"LHKir9QnPDYSBjcA4Jzq7IBRQrKRF/YKC/UiGI2OMUTB9DF2IpTFWooGEm3WNtY7\n" +
"r97x3OPNAQjg4Jius/OpbSoOwsxC8Wb44Kw/lTPCwJ4EGBMKAM0FgmhtEywJECtT\n" +
"oxFC+nyfFqEEeshC8VthZwiw1D6OK1OjEUL6fJ8CmwKiIAQZEwoABgWCaG0TLAAK\n" +
"CRArU6MRQvp8n5aqAgjS3GZHO7RIJji2zn6Vo7r0C3IXhBx/+ZDmgAEJxZT1WGYR\n" +
"N9Vjii5tpGIKem6U5ChptHulYvFlK+nvnQfpADFSJQIJAQV7tMmyeTsTw8IOFMAJ\n" +
"bxaRU+Bn9X3WqcNugzaBhNREW81WFGnRKd2zEK+GY1FEnZvIOj1UoQSrpjPiYAxz\n" +
"alH9AACLIgII29p8NxOFdMGvvRNcQ5tAr32flqDHD62K3rOYo0qwI1D3OYL6xk6v\n" +
"cjdoLoLoFa5sDse+4QffjJ9UxsEw+Uye85wCCQEaPDUdeLFLzwcqJQp3X7a3Ok1h\n" +
"aV3MYf5NYSrTGGVk8KT7xWZOaLCDWdtAF/kteBT/Prg6NpTBZH8fAbItm3KWks6X\n" +
"BGhtEywSBSuBBAAjBCMEAVBY8RHg8B13ra/HoWvvcNG5gl0yVMhSSSVkjJfyoWyK\n" +
"CMd3OH4fBti+Z+nllPHPlgAaw/6UMQoNq242LWtr+Y/XAX0G2snCs5dWG+biNaee\n" +
"QWvRkL/26KVwAmV1AsIfY577TXva/0y7Dj3hnAO/+x383cCAqwAsjsXrDWoscuf/\n" +
"LLxOAwEKCcK8BBgTCgAqBYJobRMsCRBcxJwrVFEM0hahBLBO62CL4VLDpYpOZVzE\n" +
"nCtUUQzSApsMAACuNQIJAQz2sTSbfeERmHa97smijy+Kqt8ZtDQ+k7vXOMSFGlVO\n" +
"mz84Kzs1+it62a+PaNPM1UnrcRE4NmppCtnVIS6kV8z7AgkBMcH5EeBCe10fbqdm\n" +
"BI0at8JPONR1s7yZ7R7z+G1EsnUkF+t3dzQrCOBlXI83KC+hb5eOXKURkfiZgdpO\n" +
"oR99QtE=\n" +
"=1kU8\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
private static final String v6_curve25519 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: 075CE2AD 721B3A3E 877329F1 24131A25 A43876BC DD09943D\n" +
"Comment: B1EB5C73 4E98EFEF\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xioGaG0TTxsAAAAgAhlYynX3JdyxowVByJP29cYt7tTRVtMEHzj3ctWRaNvNHEFs\n" +
"aWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CwAIGExsKAAAAUwWCaG0TTyKhBgdc\n" +
"4q1yGzo+h3Mp8SQTGiWkOHa83QmUPbHrXHNOmO/vApsBBRUKCQgLBRYCAwEABAsJ\n" +
"CAcJJwkBCQIJAwgBAp4JBYkJZgGAApkBAAAAAD2DIFbG7ai9HSJKlRqtv1c8KTXc\n" +
"7PcJeF0wjKcqxrSCp2Gwhd4/CdfHEeDWxE7x0QRuXERQJLPwae1dYITvsBrk7cR4\n" +
"DpMe33Kqaj2KuWsPWhU5efaIZ68hxurXPH1eHjpXAc4qBmhtE08bAAAAINBT+USX\n" +
"w4Y/WFM6ArUXFrjZAV8+FyUa2aZJps3zZmuiwsB1BhgbCgAAAMYFgmhtE08ioQZB\n" +
"a272wKwg9+XBs4b15pDGecNCy5kqOeZzJrWLET546gKbApkgBhkbCgAAACkioQZB\n" +
"a272wKwg9+XBs4b15pDGecNCy5kqOeZzJrWLET546gWCaG0TTwAAAABLmSA4FypT\n" +
"lEiKE1Jd8isuE3+gXWRZO+/tDkrwsar5dTUFysv0NR+LDOYy/a/HbPCDhEIKjBcY\n" +
"qzTP+3R2ZZe2rGkE+77sht2j1YIcBlzm+t3HA8V20iFn4rpWHy0571hVpggAAAAA\n" +
"Bt0gnCR4K4AuIH7fZr4b9BVJ3k4kIvdk9X7S25yyjw6JxZH17fluPiJQVCu/m0be\n" +
"lj0dfqCHfTPJ2iZl3RLu0e5mBoolbN4JostQ9FzVi9dZ788jYXvnGIUnoBRu57ZO\n" +
"n3UAzioGaG0TTxkAAAAgDRJh03RIU+tZWnyRk684h/GqvO7zL8LJFxL/lHEruULC\n" +
"mwYYGwoAAAAsBYJobRNPIqEGo8G75Tf7oWt8oa59iQ32S6uKCXrpadeS/Xjdt1iE\n" +
"zyECmwwAAAAAQPQgn2hzfU/3y0CkbuuHn0+jdFLJgs8hFpDx0v1cLpiAKvWLBIZI\n" +
"sixI3KLQxiqvxZQXCg+/dQoNO1se4XZ/g6KLvGLzJ6BC5MpUfOfJoGc23u68G2K3\n" +
"WpYP1EQ/UjACBT4I\n" +
"=Uq9u\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
private static final String v6_curve448 = "-----BEGIN PGP PUBLIC KEY BLOCK-----\n" +
"Comment: 212F1BC9 7FF7DBF0 B6F18D83 F59BB2BC 075F26C3 2CE5F150\n" +
"Comment: 0B770469 11A0A196\n" +
"Comment: Alice <alice@pgpainless.org>\n" +
"\n" +
"xkMGaG0TXxwAAAA5ffh5M4YFiH+SEFbP8SeW7FhJns/56XYfUjk28nejYWQgl4Fd\n" +
"vihxLAtryw6iEscMoudI7NsUWC+AzRxBbGljZSA8YWxpY2VAcGdwYWlubGVzcy5v\n" +
"cmc+wsA0BhMcCgAAAFMFgmhtE18ioQYhLxvJf/fb8LbxjYP1m7K8B18mwyzl8VAL\n" +
"dwRpEaChlgKbAQUVCgkICwUWAgMBAAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZ\n" +
"AQAAAAChXiCvjG1wSHZzZwaVEnfGyMGrhb/l9mjULQFaQmdEgJ33i/iEv6MQYaBS\n" +
"nPRCcI9GLjq/kkz957EhvBxeM1K5OHUAhHLy2AY4ReKB/N5Xz3D7pAivqsQzq/DS\n" +
"gIKfGQxjEt/QPy7C+qsNNHsss4l2mo3ugdV5mQV4DcBzeHJcz7hMce00HRIVRemw\n" +
"JuPhh3gEFN8hAM5DBmhtE18cAAAAOTs6skyf+PcX3A+rsPMjmEGVc1IqJw4xOqoZ\n" +
"gPghbYGGqBrYAqzr//xKshyewSMOH5fmGX8aQyoPgMLA2gYYHAoAAAD5BYJobRNf\n" +
"IqEGC4EDngLrM73LIg5bZQKYVUAM98vVI86Qq6SE4mUxlxkCmwLACyAGGRwKAAAA\n" +
"KSKhBguBA54C6zO9yyIOW2UCmFVADPfL1SPOkKukhOJlMZcZBYJobRNfAAAAAHuS\n" +
"IBQQaYywrO5u+n8R7Xcb+WoQ7FtHUmHpW5triJ/tkwfnufiC56CD465EyLJ9g+Mm\n" +
"IA+5n03AxeH0Yk7afFBgRw9pQlPb2/1P66iexPV7445sjJ9jCK2NTJaAd/H6R+CW\n" +
"iCYl5VCtkoryXNN4yad6OVt+TrEQHCF4vRUpAUAF30EO8LUuUsB0F3D0D0aDglBi\n" +
"tiAAAAAAAAR4ILw9d7lfCTtaOpBEr0YHo8kPzVcBxdDbUfo9UAqScZhwCbSVrANI\n" +
"CJw614Ss3WhTuV+l465zwccF2xh0D+VAue1AwnwnQzp518zFqTXJmOUcs7YuPWyU\n" +
"iQEA/tc+jUg9XL6EQ/5UYMhfj7nO/b8uZuD2KYPtc2e8X3mk1is05fhB9JSOOGib\n" +
"d6su8g3Pv+HQGx4AzkIGaG0TXxoAAAA4WAHz84E3zX2mIoVBETPWdzvumbTqFiUM\n" +
"uiI96b6XmWi21G8F/BdpWs1Gg347x7PbVWfq9SD7t6TCwA0GGBwKAAAALAWCaG0T\n" +
"XyKhBq9bQBVplmkyzKWaEqIweNfJ85ls16CfR2rNXYYs2vDyApsMAAAAAPQUIKzs\n" +
"xWkiYn0kM7FsHGMlMGET0wrdKg4F45dGCHDUHeUZSusp5Zs7okmY6HP4tePSMuMq\n" +
"9whjVqimGN6Xx0ybErP/ku6jExwN6QtYohZempWCZXv3SJteGI8ADuVKmcNT9MBN\n" +
"ic3vixr3vjkXsuUwBQDGDb7SRfA7DGh4mXxAaB5gUwQC+wSIG6D7gIP2Hz0fVj4A\n" +
"=9WFq\n" +
"-----END PGP PUBLIC KEY BLOCK-----\n";
@FuzzTest(
maxDuration = "60s"
)
public void verifyFuzzedSig(FuzzedDataProvider provider) throws IOException {
byte[] sig = provider.consumeRemainingAsBytes();
if (sig.length == 0) {
return;
}
try {
List<Verification> verifs = sop.verify()
.cert(v4_ed25519.getBytes(StandardCharsets.UTF_8))
.cert(v4_rsa4096.getBytes(StandardCharsets.UTF_8))
.cert(v4_nistP256.getBytes(StandardCharsets.UTF_8))
.cert(v4_nistP384.getBytes(StandardCharsets.UTF_8))
.cert(v4_nistP521.getBytes(StandardCharsets.UTF_8))
.cert(v6_curve25519.getBytes(StandardCharsets.UTF_8))
.cert(v6_curve448.getBytes(StandardCharsets.UTF_8))
.signatures(sig)
.data(data);
if (verifs.isEmpty()) {
return;
}
for (Verification v : verifs) {
System.out.println(v.toString());
}
} catch (SOPGPException.NoSignature e) {
// ignore
} catch (SOPGPException.BadData e) {
// expected
}
}
}

View file

@ -0,0 +1,21 @@
-----BEGIN PGP PRIVATE KEY BLOCK-----
xVgEaGfc0BYJKwYBBAHaRw8BAQdATOdV6wukmqSavJGlnqhkk0fvPzhNnzvp3sv5
gMq3BzUAAP9jcz6ydPE5rdNwUAxxOVn2stxGntC6mm0m9RI+1hYhbRCQzRxBbGlj
ZSA8YWxpY2VAcGdwYWlubGVzcy5vcmc+wp8EExYKAFEFgmhn3NAJEEjYL/TjDku0
FqEE3bruvdTZy1Xd+RNsSNgv9OMOS7QCmwEFFQoJCAsFFgIDAQAECwkIBwknCQEJ
AgkDCAECngkFiQlmAYACmQEAAKq4AQC98RHIFbs5pyeWpKimjP3LFZ6rroorBcMc
izPAPqXEjQD+N5DSn/TJeKeld6KCB69W2KSj4BBdPvabLG6cRq+/8AbHWARoZ9zQ
FgkrBgEEAdpHDwEBB0A9Q+9+bvKSI1voDq+rb7yKjCVKVG4jO1qj/wTPF5PkSQAB
AIP7XckHeFuJ8qnp2hU2keb+Xzo+8ZlR57mj37uAuQUzEibCwBgEGBYKAIoFgmhn
3NAJEERXJjHxSbsAFqEEFQlMncw3fOJ/EBmfRFcmMfFJuwACmwJfIAQZFgoABgWC
aGfc0AAKCRBEVyYx8Um7AEw5AQDkSgGU1E6rmHO2DTj1jNXkD8k3rnyxxjP66RLi
whLVYwD9FwWQ5kXwM2qFheWbuMN7sghEt+SDQHc7tgwgnWppEwAAAGNHAQDM4ncO
o05rjfFVAaTFJ26Ixm8MK2HnGl+fQT73wyDJEgD/U/g+b7bppnSF4scuZMUSA9xQ
oyK3b5J5uNqfj+mxpgDHXQRoZ9zQEgorBgEEAZdVAQUBAQdAdEmUSz3XlPFa+u8I
0hJu+RAuJO0Sj64+ZlvawGsQhFcDAQgHAAD/Y78qz+fyfkEa6DjwhhLdZzKWJlII
KL1oW1vsNE8SVEAPGMJ4BBgWCgAqBYJoZ9zQCRBqhz9va/rd1hahBOfnmVV9LWWf
y/UZPWqHP29r+t3WApsMAADktwD/TSz5ZK5WFaaLSzpIwyA2U9XvgUvGD+5bSban
XsYU9LwA/1NWshgcBfDMRoboZGPwWIikfRUOatGph4IRj/5UZmIJ
=n5sh
-----END PGP PRIVATE KEY BLOCK-----

View file

@ -0,0 +1,21 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: DDBA EEBD D4D9 CB55 DDF9 136C 48D8 2FF4 E30E 4BB4
Comment: Alice <alice@pgpainless.org>
xjMEaGfc0BYJKwYBBAHaRw8BAQdATOdV6wukmqSavJGlnqhkk0fvPzhNnzvp3sv5
gMq3BzXNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CnwQTFgoAUQWCaGfc
0AkQSNgv9OMOS7QWoQTduu691NnLVd35E2xI2C/04w5LtAKbAQUVCgkICwUWAgMB
AAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAAqrgBAL3xEcgVuzmnJ5akqKaM
/csVnquuiisFwxyLM8A+pcSNAP43kNKf9Ml4p6V3ooIHr1bYpKPgEF0+9pssbpxG
r7/wBs4zBGhn3NAWCSsGAQQB2kcPAQEHQD1D735u8pIjW+gOr6tvvIqMJUpUbiM7
WqP/BM8Xk+RJwsAYBBgWCgCKBYJoZ9zQCRBEVyYx8Um7ABahBBUJTJ3MN3zifxAZ
n0RXJjHxSbsAApsCXyAEGRYKAAYFgmhn3NAACgkQRFcmMfFJuwBMOQEA5EoBlNRO
q5hztg049YzV5A/JN658scYz+ukS4sIS1WMA/RcFkOZF8DNqhYXlm7jDe7IIRLfk
g0B3O7YMIJ1qaRMAAABjRwEAzOJ3DqNOa43xVQGkxSduiMZvDCth5xpfn0E+98Mg
yRIA/1P4Pm+26aZ0heLHLmTFEgPcUKMit2+Sebjan4/psaYAzjgEaGfc0BIKKwYB
BAGXVQEFAQEHQHRJlEs915TxWvrvCNISbvkQLiTtEo+uPmZb2sBrEIRXAwEIB8J4
BBgWCgAqBYJoZ9zQCRBqhz9va/rd1hahBOfnmVV9LWWfy/UZPWqHP29r+t3WApsM
AADktwD/TSz5ZK5WFaaLSzpIwyA2U9XvgUvGD+5bSbanXsYU9LwA/1NWshgcBfDM
RoboZGPwWIikfRUOatGph4IRj/5UZmIJ
=hJD/
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1,21 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: DDBA EEBD D4D9 CB55 DDF9 136C 48D8 2FF4 E30E 4BB4
Comment: Alice <alice@pgpainless.org>
xjMEaGfc0BYJKwYBBAHaRw8BAQdATOdV6wukmqSavJGlnqhkk0fvPzhNnzvp3sv5
gMq3BzXNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CnwQTFgoAUQWCaGfc
0AkQSNgv9OMOS7QWoQTduu691NnLVd35E2xI2C/04w5LtAKbAQUVCgkICwUWAgMB
AAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAAqrgBAL3xEcgVuzmnJ5akqKaM
/csVnquuiisFwxyLM8A+pcSNAP43kNKf9Ml4p6V3ooIHr1bYpKPgEF0+9pssbpxG
r7/wBs4zBGhn3NAWCSsGAQQB2kcPAQEHQD1D735u8pIjW+gOr6tvvIqMJUpUbiM7
WqP/BM8Xk+RJwsAYBBgWCgCKBYJoZ9zQCRBEVyYx8Um7ABahBBUJTJ3MN3zifxAZ
n0RXJjHxSbsAApsCXyAEGRYKAAYFgmhn3NAACgkQRFcmMfFJuwBMOQEA5EoBlNRO
q5hztg049YzV5A/JN658scYz+ukS4sIS1WMA/RcFkOZF8DNqhYXlm7jDe7IIRLfk
g0B3O7YMIJ1qaRMAAABjRwEAzOJ3DqNOa43xVQGkxSduiMZvDCth5xpfn0E+98Mg
yRIA/1P4Pm+26aZ0heLHLmTFEgPcUKMit2+Sebjan4/psaYAzjgEaGfc0BIKKwYB
BAGXVQEFAQEHQHRJlEs915TxWvrvCNISbvkQLiTtEo+uPmZb2sBrEIRXAwEIB8J4
BBgWCgAqBYJoZ9zQCRBqhz9va/rd1hahBOfnmVV9LWWfy/UZPWqHP29r+t3WApsM
AADktwD/TSz5ZK5WFaaLSzpIwyA2U9XvgUvGD+5bSbanXsYU9LwA/1NWshgcBfDM
RoboZGPwWIikfRUOatGph4IRj/5UZmIJ
=hJD/
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1,5 @@
-----BEGIN PGP MESSAGE-----
SGVsbG8gV29ybGQK
=1hzE
-----END PGP MESSAGE-----

View file

@ -0,0 +1,9 @@
-----BEGIN PGP MESSAGE-----
wWwGFQTn55lVfS1ln8v1GT1qhz9va/rd1hIBB0CK2xMAGPBhSAztl26xmFEMGg3a
oKnwCwphhU7yshVCOTAY4zKszTvvn3CxMV8HbNQ0jr61kp9H9bwGDz8w6Nz/WQrn
S4QPVvXsLOIZd9L/KKbSWgIJAQOoAMCSkjp146L+E1uYItfRk/K4dupbQWX+jAHp
6MJvIi4rzyht7qt2C0RPPFDImzCTR9V1Ry/BCa3w4E3x3CBpFVyEy9N8mizUEJU6
TPRvAFqekyQpjg==
=EL9J
-----END PGP MESSAGE-----

View file

@ -0,0 +1,7 @@
-----BEGIN PGP SIGNATURE-----
wnUEABYKACcFgmhn3d8JEERXJjHxSbsAFqEEFQlMncw3fOJ/EBmfRFcmMfFJuwAA
AOgXAQCAXxXslQv4i9C4kAW9aOJCj7Cn7ZkMsyQvw9vTSW5R6QD9FmncQ64oOGEL
gtX9s6OFb7Bd1UEbfL7G3QS2wg0QHQc=
=o0Xk
-----END PGP SIGNATURE-----

View file

@ -0,0 +1,5 @@
-----BEGIN PGP MESSAGE-----
SGVsbG8gV29ybGQK
=1hzA
-----END PGP MESSAGE-----

View file

@ -0,0 +1,4 @@
-----BEGIN PGP MESSAGE-----
SGVsbG8gV29ybGQK
-----END PGP MESSAGE-----

View file

@ -0,0 +1 @@
ця ▌╞ёН<`╠!J]В╣,й╔┤ tс ╤р@:╗0▒╩sdЁ╠÷╒Е$X⌠²Зщ, ©bcЁv7НG┤┐█5iU▀v·X▌VнЕьЭ▌ы щ 8Р

View file

@ -0,0 +1 @@
ця ▌╞ёН<`╠!J]В╣,й╔┤ tс рС▌VнЕьЭ▌ы щ 3

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·bc³v7îG‡ƒ<1B>5iUIìæ¦l 'Š,¿½£w—Ñ/3OÖc

View file

@ -0,0 +1 @@
ця ▌╞ёН<`╠!J]В╣,й╔┤ tс ╤р@:╗0▒╩sdЁ╠÷╒Е$X⌠²Зщ, ╥bcЁv7НG┤┐█5iU▀v·X▌VнЕьЭ▌ы щ 8Р

View file

@ -0,0 +1 @@
ÃÑóæqP\Ù±!J]÷µ,Ê¥‡ ¶Ò@:¨0.sd³±Ÿ¢å$X“<58>úÝ, ·bc«v7îG‡

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·&c³v7îG‡ƒ<1B>5iUIìæ¦l 'Š,¿½£w—Ñ/3OÖc

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>ú݇ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ¿½£w—Ñ/7OÖc

View file

@ -0,0 +1 @@
Á^VÝđ]ĚVż!â7<C3A2>Qm‡'ŇçĂŮä\ĎÍk QýZ#ż80ż80Žo¤]FžZs†TAO¬<4F>•"rüyb“züăgî<67>ĎüÝńĎNś"ź<˙ ˇŇ@îi 8ň%mđZ±ˇf´h:"#:Š

View file

@ -0,0 +1 @@
Á^VÝđ]ĚVż!â7<C3A2>Qm‡'ŇçĂŮä@ĎÍk QýZ#ż80Žo¤]FžZs†TAO¬<4F>•"rüyb“züăgî<67>ńĎüÝĎNś"ź<˙ ˇŇ@îi 8ň%mđZ±ˇf´h:"#:Š

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·bc³v7îG‡ƒ<1B>ÿÿÿÿÿÿÿÿ 'Š,¿½£w—Ñ/3OÖc

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·bc³v7îG‡ƒ<1B>5iUI*ìæ¦l 'Š,¿½£w—Ñ/Ò@¥‡ c

View file

@ -0,0 +1 @@
ця ▌╞ёН<`╠!J]В╣,й╔┤ tс рС▌VнЕьЭ▌ы щ 3Р@:╗0▒╩sd╖С▌VнЕьЭ▌ы щ 3Р

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·bc³v7îG‡ƒ<1B><35>•"rüybŠ,¿½£w—Ñ/3OÖc

View file

@ -0,0 +1 @@
Á^VÝđ]ĚVż!â7<C3A2>Qm‡'ŇçĂŮä\ĎÍk QýZ#ż80Žo¤]FžZs†TAO¬<4F>•"rüyb“züăgî<67>ńĎüÝĎNś"ź<˙ ˇŇ@îikRŞŕ,`Ŕ9<>(<28>dd%vžXŽVÎĺŘüŽŮ Ý 8ň%mđZ±ˇf´h:"#:Š

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>ú—Ñ/7OÖc

View file

@ -0,0 +1 @@
Á^VÝđ]ĚVż!â7<C3A2>Qm‡'ŇçĂŮä\ĎÍk QýZ#ż80Žo¤]FžZs†TAO¬<4F>•"rüyb“züăgî<67>ńĎüÝĎNś"ź<˙ ˇŇ@kRŞŕ,`Ŕ9<>(<28>dd%vžXŽVÎĺŘüŽŮ Ý 8ň%mđZ±ˇf´h:"#:Š

View file

@ -0,0 +1,8 @@
-----BEGIN PGP MESSAGE-----
wV4DVhfd8F3MVr8SAQdAphsh4jeDfsoaUW2HJ9Lnw9nkDlzPzWsDC1ES/Vojvzgw
jm/CpF1GA55aHXOGVEFPrIGVInIc/HliApN6/ONn7oPxz/zdz06cCCKfPBT/CQeh
0kABae4Oa1KSeboCqhXgLGDAGhY5gSiYZBdkJYt2nlgOjlYHzuXY/I7ZDAjdDSA4
GPIWJW0F8FqxoWa0aDoiIzqK
=BXMn
-----END PGP MESSAGE-----

View file

@ -0,0 +1 @@
ÃÑ ޝ£î<`±!J]÷µ,Ê¥‡ ¶Ò@:¨0»sd³±Ÿ¢å$X“<58>úÝ, ·bc³v7îG‡ƒ<1B>5iUIìæ¦l 'Š,¿½£w—Ñ/7OÖc

View file

@ -0,0 +1,7 @@
-----BEGIN PGP MESSAGE-----
wx4EBwMC0Q0Zjq+j7jxgsSFKXRX3CLUsyqWHDHTTDLbSQAE6qDCRu3Nks7Gfohbl
JFiTnfrdHiwMArdiY7N2NwHuR4eDARuNNWlVSezmpmwFICeKLL+9o3eX0S83Fk8S
1mM=
=N/kL
-----END PGP MESSAGE-----

View file

@ -0,0 +1,17 @@
-----BEGIN PGP MESSAGE-----
wcFMA6SJex80gcWaAQ/8C4tRTyBQhmtBERKS0VHc/nb7cS2dbr7XSMpvk53Fj9Se
qWgJloepmkFea0OGy+ectRwGFWj3Aj0bdChRbi3ipfX1QXdhAbdBTb3+GlfPkr6X
WfywYYMbKB2eoDpSw5sdgvN3TKOlSL/8snGtrkZYy3VoS6xZ9RRhrmz9KEw6q2vc
APz4ldOOMmYTgDT8o8gjqqc+7hK2Ac+jdIq6Jtmrs49yxONU92UL8ormJbGpOFVB
+tAUIgFHib/0qc6ch5KhcXJUCT/jt7cn+BoLk4U2ns9mBxwnYJ+vnxzFrJxgySY0
TOOGxmuPQOCMZfOxme0HUROxu5xiSM6PRLWilL6YRyw3IXekfVGNeFHnsF/Rxx7c
3v9cvnsDQpGE9qRsgtxcvCGJkKnPNFRQPA/vpXB/GhrBtGLSpTMKHxbHyPsb10ZP
IN5aR37QqN7LLu1vlFdjz37CL0ib4pHEH1FvpsaY9l+nzv3eltF2U7aFHHdGQd7E
mmihSbdzRTYys2yX2P/mYVZo2j/GciGUtd9ucllouTRlBEL1IEgBSeSuaH9xYscN
u4rTDgezBS8WF9/0T8elmeTjk2RRojRGKt98HFl/YU8V/MLTDS/c7Hl6U9FIpGrM
QIFzunmf4npwrgAEH+spzl6qVYygEqDHTt1K05eEO4ZxoHBwTd9dTgRtj/b35lHS
QAHc37MCFlJVNma77AinyVs17c2kgUP+gV5Liwt07kSRca7hzYJgYDvQi2hf08pP
abmx0ChGu+JRUAvkFD7k120=
=CaPm
-----END PGP MESSAGE-----

View file

@ -0,0 +1,21 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: DDBA EEBD D4D9 CB55 DDF9 136C 48D8 2FF4 E30E 4BB4
Comment: Alice <alice@pgpainless.org>
xjMEaGfc0BYJKwYBBAHaRw8BAQdATOdV6wukmqSavJGlnqhkk0fvPzhNnzvp3sv5
gMq3BzXNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CnwQTFgoAUQWCaGfc
0AkQSNgv9OMOS7QWoQTduu691NnLVd35E2xI2C/04w5LtAKbAQUVCgkICwUWAgMB
AAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAAqrgBAL3xEcgVuzmnJ5akqKaM
/csVnquuiisFwxyLM8A+pcSNAP43kNKf9Ml4p6V3ooIHr1bYpKPgEF0+9pssbpxG
r7/wBs4zBGhn3NAWCSsGAQQB2kcPAQEHQD1D735u8pIjW+gOr6tvvIqMJUpUbiM7
WqP/BM8Xk+RJwsAYBBgWCgCKBYJoZ9zQCRBEVyYx8Um7ABahBBUJTJ3MN3zifxAZ
n0RXJjHxSbsAApsCXyAEGRYKAAYFgmhn3NAACgkQRFcmMfFJuwBMOQEA5EoBlNRO
q5hztg049YzV5A/JN658scYz+ukS4sIS1WMA/RcFkOZF8DNqhYXlm7jDe7IIRLfk
g0B3O7YMIJ1qaRMAAABjRwEAzOJ3DqNOa43xVQGkxSduiMZvDCth5xpfn0E+98Mg
yRIA/1P4Pm+26aZ0heLHLmTFEgPcUKMit2+Sebjan4/psaYAzjgEaGfc0BIKKwYB
BAGXVQEFAQEHQHRJlEs915TxWvrvCNISbvkQLiTtEo+uPmZb2sBrEIRXAwEIB8J4
BBgWCgAqBYJoZ9zQCRBqhz9va/rd1hahBOfnmVV9LWWfy/UZPWqHP29r+t3WApsM
AADktwD/TSz5ZK5WFaaLSzpIwyA2U9XvgUvGD+5bSbanXsYU9LwA/1NWshgcBfDM
RoboZGPwWIikfRUOatGph4IRj/5UZmIJ
=hJD/
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1,21 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Comment: DDBA EEBD D4D9 CB55 DDF9 136C 48D8 2FF4 E30E 4BB4
Comment: Alice <alice@pgpainless.org>
xjMEaGfc0BYJKwYBBAHaRw8BAQdATOdV6wukmqSavJGlnqhkk0fvPzhNnzvp3sv5
gMq3BzXNHEFsaWNlIDxhbGljZUBwZ3BhaW5sZXNzLm9yZz7CnwQTFgoAUQWCaGfc
0AkQSNgv9OMOS7QWoQTduu691NnLVd35E2xI2C/04w5LtAKbAQUVCgkICwUWAgMB
AAQLCQgHCScJAQkCCQMIAQKeCQWJCWYBgAKZAQAAqrgBAL3xEcgVuzmnJ5akqKaM
/csVnquuiisFwxyLM8A+pcSNAP43kNKf9Ml4p6V3ooIHr1bYpKPgEF0+9pssbpxG
r7/wBs4zBGhn3NAWCSsGAQQB2kcPAQEHQD1D735u8pIjW+gOr6tvvIqMJUpUbiM7
WqP/BM8Xk+RJwsAYBBgWCgCKBYJoZ9zQCRBEVyYx8Um7ABahBBUJTJ3MN3zifxAZ
n0RXJjHxSbsAApsCXyAEGRYKAAYFgmhn3NAACgkQRFcmMfFJuwBMOQEA5EoBlNRO
q5hztg049YzV5A/JN658scYz+ukS4sIS1WMA/RcFkOZF8DNqhYXlm7jDe7IIRLfk
g0B3O7YMIJ1qaRMAAABjRwEAzOJ3DqNOa43xVQGkxSduiMZvDCth5xpfn0E+98Mg
yRIA/1P4Pm+26aZ0heLHLmTFEgPcUKMit2+Sebjan4/psaYAzjgEaGfc0BIKKwYB
BAGXVQEFAQEHQHRJlEs915TxWvrvCNISbvkQLiTtEo+uPmZb2sBrEIRXAwEIB8J4
BBgWCgAqBYJoZ9zQCRBqhz9va/rd1hahBOfnmVV9LWWfy/UZPWqHP29r+t3WApsM
AADktwD/TSz5ZK5WFaaLSzpIwyA2U9XvgUvGD+5bSbanXsYU9LwA/1NWshgcBfDM
RoboZGPwWIikfRUOatGph4IRj/5UZmIJ
=hJD/
-----END PGP PUBLIC KEY BLOCK-----

View file

@ -0,0 +1 @@
Ζ&hpΥΗΊ›Σπ"µϋ^<5E><11>Έh­κεeh„(ΐύ

View file

@ -0,0 +1 @@
фVhp*├Hн=$И■;а*вnх(У╧²cчЩy▐╨╝НЭ4 ,▒sU;хТь эй#!┘прA╩gДjL< ▐B▌7?

View file

@ -0,0 +1,2 @@
ÆRhp*†HÎ=-ä $öí5?» <0C>¡¥xÄH4—ÅÌt?“Ïè’%<25>[sTpXý
8ãG:I)I/a¥¦"‡ÌyA9

View file

@ -0,0 +1 @@
<EFBFBD><03>vvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvvv

Some files were not shown because too many files have changed in this diff Show more