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

Even more fuzzing

This commit is contained in:
Paul Schaub 2025-07-09 14:09:47 +02:00
parent 891a6b745a
commit 68991800d4
Signed by: vanitasvitae
GPG key ID: 62BEE9264BF17311
37 changed files with 538 additions and 409 deletions

View file

@ -145,7 +145,12 @@ 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)
}
packet
} != null) {
@ -208,12 +213,24 @@ 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,
literalData.modificationTime,
StreamEncoding.requireFromCode(literalData.format))
layerMetadata.child = LiteralData(fileName, literalData.modificationTime, streamEncoding)
nestedInputStream = literalData.inputStream
}
@ -223,11 +240,15 @@ 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),
layerMetadata.depth + 1)
val compressionLayer = CompressedData(compAlg, layerMetadata.depth + 1)
LOGGER.debug(
"Compressed Data Packet (${compressionLayer.algorithm}) at depth ${layerMetadata.depth} encountered.")
@ -326,32 +347,19 @@ class OpenPgpMessageInputStream(
syntaxVerifier.next(InputSymbol.ENCRYPTED_DATA)
val encDataList = packetInputStream!!.readEncryptedDataList()
val esks = ESKsAndData(encDataList)
when (EncryptedDataPacketType.of(encDataList)!!) {
EncryptedDataPacketType.SEIPDv2 ->
LOGGER.debug(
"Symmetrically Encrypted Integrity Protected Data Packet version 2 at depth " +
"${layerMetadata.depth} encountered.")
EncryptedDataPacketType.SEIPDv1 ->
LOGGER.debug(
"Symmetrically Encrypted Integrity Protected Data Packet version 1 at depth " +
"${layerMetadata.depth} encountered.")
EncryptedDataPacketType.LIBREPGP_OED ->
LOGGER.debug(
"LibrePGP OCB-Encrypted Data Packet at depth " +
"${layerMetadata.depth} encountered.")
EncryptedDataPacketType.SED -> {
LOGGER.debug(
"(Deprecated) Symmetrically Encrypted Data Packet at depth " +
"${layerMetadata.depth} encountered.")
LOGGER.warn("Symmetrically Encrypted Data Packet is not integrity-protected.")
if (!options.isIgnoreMDCErrors()) {
throw MessageNotIntegrityProtectedException()
}
if (encDataList.isEmpty) {
LOGGER.debug("Missing encrypted session key packet.")
return false
}
if (!encDataList.isIntegrityProtected && !encDataList.get(0).isAEAD) {
LOGGER.warn("Symmetrically Encrypted Data Packet is not integrity-protected.")
if (!options.isIgnoreMDCErrors()) {
throw MessageNotIntegrityProtectedException()
}
}
val esks = ESKsAndData(encDataList)
LOGGER.debug(
"Encrypted Data has ${esks.skesks.size} SKESK(s) and" +
" ${esks.pkesks.size + esks.anonPkesks.size} PKESK(s) from which ${esks.anonPkesks.size} PKESK(s)" +
@ -583,7 +591,14 @@ 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)
}
}
fun readOnePassSignature(): PGPOnePassSignature {

View file

@ -15,6 +15,8 @@ import org.pgpainless.decryption_verification.syntax_check.State
* @see [RFC4880 §11.3. OpenPGP Messages](https://www.rfc-editor.org/rfc/rfc4880#section-11.3)
*/
class MalformedOpenPgpMessageException : RuntimeException {
constructor(cause: Throwable) : super(cause)
constructor(message: String) : super(message)
constructor(message: String, e: MalformedOpenPgpMessageException) : super(message, e)

View file

@ -165,13 +165,13 @@ class SignatureUtils {
// having them compressed,
// except for an attacker who is trying to exploit flaws in the decompression
// algorithm.
// Therefore, we ignore compressed data packets without attempting decompression.
// Therefore, we ignore compressed data packets without attempting
// decompression.
if (nextObject is PGPCompressedData) {
// 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) {

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() }
headerMap
.getOrPut(HEADER_COMMENT) { mutableSetOf() }
.add(it.replace("\n", "\\n").replace("\r", "\\r"))
}
// X-1 further identities