#1105820 Gnupg-in-debian considers comment packets invalid

Package:
gnupg
Source:
gnupg
Submitter:
Sune Stolborg Vuorela
Date:
2025-05-27 19:21:01 UTC
Severity:
normal
#1105820#5
Date:
2025-05-15 10:19:32 UTC
From:
To:
Package: gnupg
Version: 2.4.6-7
Severity: important

Hi

Since
https://salsa.debian.org/debian/gnupg2/-/blob/debian/unstable/debian/patches/
freepg/0019-Disallow-compressed-signatures-and-certificates.patch?
ref_type=heads#L188

GnuPG-in-debian has stopped accepting comment packets in detached signatures.
That should be fully valid and there is no good reason to consider them bad.

It is at least used by poppler to pad signature fields when adding a detached
gpg signature in a pdf document, and these can't be re-validated on a Debian
system.

This change also breaks the part of Poppler's CI system that is based off
randomly regenerated Debian containers. (and they were very recent
regenerated)

Please quickly fix this also for the trixie release.

/Sune

#1105820#10
Date:
2025-05-15 12:04:43 UTC
From:
To:
Also, occasionally the poppler test suite actually still passes, so there is
something fishy in that 0019 patch.

Rebuilding without that patch makes it consistently succeed.

A data file is used here, and the signature can be cut out with pdfsig.

https://gitlab.freedesktop.org/poppler/test/-/raw/
91ee031c882634c36f2f0f2f14eb6646dd542fb9/unittestcases/some-text-
pgp_signed.pdf?inline=false

The signature blob is a detached signature packet followed by a finite length
comment packet.

/Sune

#1105820#15
Date:
2025-05-16 10:01:38 UTC
From:
To:
A comment package here is what GnuPG calls PKT_COMMENT, it has datavalue 61.
And in most/all openpgp (and librepgp) specs this is in the private	/
experimental range, so they should not be rejected 'just because'.

This was btw introduces in GnuPG in 1998 and has been in use for things ever
since.

/Sune

#1105820#20
Date:
2025-05-16 14:33:28 UTC
From:
To:
Hi Sune--

Thanks for this report.  Do you know what tooling is generating these
packets for poppler?

In GnuPG right now (even without the patch you identified), those
packets are generally ignored.

The OpenPGP packet grammar in RFC 4880 doesn't describe a detached
signature object at all, so it was really anyone's guess what a detached
embedded signature is supposed to be.  There is no obligation on any
OpenPGP parser to accept arbitrary packets in arbitrary locations.

The OpenPGP working group collaboratively described what a detached
signature in RFC 9580:
https://www.rfc-editor.org/rfc/rfc9580.html#section-10.4 if there was
some other grammar being used by someone, there were literally years for
them to speak up.

Looking at your sample PDF (thanks for the link!) it appears that it is
a comment packet of length 0x24d4 containing all zeros.  What is the
purpose of this packet?  Why is it being included?

Rather than increasing the attack surface of GnuPG, maybe whatever
implementation is producing this thing shouldn't emit a
private/experimental packet sequence.

This is the first place i've seen these particular experimental packets
show up.  If there are other places where you know of it being used,
please let me know!

#1105820#25
Date:
2025-05-16 14:54:46 UTC
From:
To:
Hi Sune--

I just found:

https://gitlab.freedesktop.org/poppler/poppler/-/commit/f0316ba62df9ce6d8e17a9349934b4a06df87e69

It looks like this was added very recently, well after RFC 9580 came
out, and injects these arbitrary private/experimental packets, strictly
for padding purposes.

Why is padding needed in this case?

In section 10.4, RFC 9580 explicitly observes that for detached
signatures (which i think is the closest characterization of the thing
you're looking to add to the PDF standard here):

If padding is what's needed here, i recommend using standardized padding
that any compliant OpenPGP implementation will accept.  Presumably the
goal is for arbitrary OpenPGP consumers to be able to verify the
signature of the PDF, right?

Alternately, if you want to pad a signature packet, you can also inject
arbitrary non-critical subpackets into the unhashed subpacket area of
the signature itself; those subpackets should be ignored by every
OpenPGP implementation i'm aware of.

All of this padding flexibility, of course, enables the production of an
arbitrary covert sidechannel in these signature formats.  But as you
say, those sidechannels have been part of OpenPGP for ages.

The GnuPG patchset offered by Demi Marie tightens up OpenPGP's packet
grammar significantly, reducing the attack surface.

I'd really prefer to not drop that security enhancement to accomodate
recently introduced changes in unrelated software that are themselves
not compliant with any standards i'm aware of.

#1105820#30
Date:
2025-05-16 17:41:54 UTC
From:
To:
Hi Daniel

Thank you for your reply.

I'm not sure why all of this matters; there are others that expects gnupg in
Debian to validate and fail things in a similar way to gnupg-from-upstream and
gnupg-in-other distributions.

I don't think that is an unreasonable request.

And poppler is released, used by others. Not only in Debian, but also in other
places, and it generates these documents. We should not reject them just
because we can.
It has also been implemented based on user requests.

But nevertheless, let me try to at least address some of your questions. Given
that I actually wrote and pushed the code in question to poppler, I actually
know about a lot of 'why'.

Note that it is using GpgME to talk to GnuPG, not to do RFC9580 which GnuPG
does not support, so all references to RFC9580 is not relevant here.

Also note that this is more or less piggy-backing on top of the S/Mime (X509)
implementation that talks to gpg for S/Mime (X509) handling.

And these private packets are fully compliant. It is in the spec after all.
And as I wrote, these packets have been around since 1998, which is at least
way before I heard of GnuPG, let alone co-maintained it in Debian.

In a the signed part of a PDF file, it is described that the signature is
stored in a specific part of a PDF file (The signature covers bytes A to B and C
to D (and the signature needs to be stored between B and C)), so there this
hole that contains the signature, but given the location in the file needs to
be specified before signing the data, it must be bigger than the expected
signature size.

There was more or less 4 ways of doing it
1) Do a gpg packet parser in poppler to only pass the actual signature part of
the signature area on to GnuPG
2) Wrap it in an extra level of indirection with some kind of (datasize, data)
structure
3) Use something the underlying implementation would happily accept and
discard of a variable size to make it fit in the middle of the pdf file.
4) Sign some dummy data first with the key in question, check the size of the
signature and hope that a repeat signature of different data will have the
exact same size.

4) involves the word 'hope' and is thus a bad idea. Also for people not using
an agent to remember passphrases or using some kind of touch-to-sign
smartcard, it is an annoying and surprising workflow ("Why do I need to sign
twice").

Given that there already is a battle tested package parser in GnuPG, makes 3)
a much better choice than 1.

Using 3) would also make it easier for e.g. pdfsig or just 'cut' to dump the
signature into something that one manually can process with GnuPG, just like
how you can do it with S/Mime signatures and process it manually with openssl,
gpgsm or whatever tool you want to do without any other faffing around, so that
kind of is points against 2.

One could maybe use multiple 'marker' packets, but they seem to be fixed length
packets just complicating it again.

So simplicity was chosen. Towards the library-implementation that was already
in the stack.

Currently, the goal is for users to be able to sign and validate documents
without having a properly issued (likely governmental) S/Mime certificate on
multiple operating systems.

But that would require understanding the packets rather than just passing it
on to the underlying implementation, thus complicating the code and increase
complexity of poppler.

A packet parser alone would likely double the amount of code needed in Poppler
for this feature.



/Sune

#1105820#35
Date:
2025-05-16 22:25:31 UTC
From:
To:
Hi Sune--

Thanks for following up here.

Debian also has a responsibility to reduce the attack surface of the
software it ships, even when upstream ignores reported risks and
vulnerabilities.

I'm going to walk through the timeline here, in case it's useful for
someone reading the archive.

The patch that you identified as problematic was offered to upstream,
and a CVE was indicated, with the intent to reduce the attack surface of
GnuPG.  There was no substantive response to this post for about three
years on the upstream bugtracker:

https://dev.gnupg.org/T5993

Soon after Demi Marie reported the concern and offered a fix which
pushed back on accepting arbitrary packets, GnuPG upstream was publicly
warning against accepting *any* sort of padding-style packet, despite
the OpenPGP working group explicitly trying to adopt such a mechanism:

https://mailarchive.ietf.org/arch/msg/openpgp/npCHOnOWEWVfvztmrkqs0w00NLk#

From that thread:

So it appears that to the extent that GnuPG upstream wants to avoid
leaks, they will *reject* padding packets that could serve as a side
channel.

Meanwhile, Werner had an opportunity during that discussion, to propose
that we should accept either packet type ID 16 (known in GnuPG as
PKT_OLD_COMMENT) or *any* experimental packet codepoint as a padding
packet.  But he didn't, leading me to believe that GnuPG would be
rejecting extra channels.

Your poppler changes stem from commits a few months ago, that *adopt* a
padding packet, in contravention of GnuPG's stated intentions:

https://gitlab.freedesktop.org/poppler/poppler/-/commit/f0316ba62df9ce6d8e17a9349934b4a06df87e69

And, it looks like at least parts of Demi Marie's proposed fix from
T5993 are flowing into GnuPG upstream now, but not all of it.  At least
part of what is being held out (not adopted) is exactly the part that
implements what Werner suggested a careful OpenPGP implementation should
do.  And poppler is taking advantage of that gap between intention and
action.

Am i missing anything from this timeline?

How long as poppler been released with this functionality?  Who is using
it?  Has it been tested against any other PDF + OpenPGP implementation?

The codebase says "PGP signatures".  yes, i agree that it's using GpgME,
but if we're talking about PGP, let's keep the terms straight.  The hope
is to be interoperable with other PGP implementations, right?

What is in the spec?  Public key packets are *also* in the spec, but
they don't belong in a detached signature, which is what is described
here.  Nothing in any OpenPGP specification i've ever read suggests that
arbitrary packets can be included in any position in a detached
signature.  Indeed, GnuPG itself wouldn't accept private/experimental
packet 62 in that position, if i'm reading the code right.

Where were they in 1998?  The ones you're raising as a must-fix scenario
about look fairly novel to me, less than a year old in Poppler,
apparently released without interoperability testing.

Thanks for this breakdown.  I agree it seems like a challenging
engineering problem with a bunch of finicky constraints.  It seems like
the discussion on how to do it safely in poppler might belong over on
https://gitlab.freedesktop.org/poppler/poppler/-/issues/1595

The point of T5993 is that it is *not* in fact a battle-tested packet
parser.  Rather, GnuPG has been shipping a much broader attack surface
than it needs to for years, *and* has been declining to reduce the
attack surface even following sensible recommendations from their lead
developer.  Please don't use recent changes in Poppler to discourage
further improvements in GnuPG or other OpenPGP implementations.

I fully support this goal, and i'd love to see it happen in a way that
is effective and interoperable.  Happy to follow up on
https://gitlab.freedesktop.org/poppler/poppler/-/issues/1595 if you'd
like to try to figure out how to do that safely.

yes, agreed.  It looks like you're now working for g10code.  Perhaps the
way to improve this is to update GnuPG to be able to specify a target
size of the emitted packet?

I agree that asking Poppler to manually parse OpenPGP packets would be a
mistake.

Regards,

#1105820#40
Date:
2025-05-17 18:01:48 UTC
From:
To:
Let me ask you a completely different question. What is - to you - the purpose
of the reserved packet space around 61-63 in any of the pgp related standards?
What's the purpose of X-foo headers in emails? of X-foo http headers?

This is not about poppler or rfc9580 or any other things. This is about GnuPG-
in-debian adding a patch that was rejected upstream for likely breaking
things.

I consider this in the same family as if an email server started to reject
emails with a X-foo header, a http client refusing to continue downloading
after a X-foo header.


/Sune

#1105820#45
Date:
2025-05-18 03:12:33 UTC
From:
To:
Hi Sune--

It's not really up to me, for what it's worth.  I'm basing my answers
on:

https://www.rfc-editor.org/rfc/rfc9580.html#section-12.10

which references:

https://www.rfc-editor.org/rfc/rfc8126.html#section-4.1 (Private use)
https://www.rfc-editor.org/rfc/rfc8126.html#section-4.2 (Experimental use)

(or, if you prefer the obsoleted RFC 4880,

https://datatracker.ietf.org/doc/html/rfc4880#section-13.10

which references: https://datatracker.ietf.org/doc/html/rfc2434#page-5 )

But if you want my short paraphrase, it's this:

 - Private means "i will use this only when talking to other people
   within the same administrative domain, and i don't care about
   interoperability."

 - Experimental means "i might at some point want to bother with
   interoperability, but i need to experiment with some functionality
   without burning a finite codepoint; i don't intend these generated
   objects to have any permanence or persistence."

For example, the OpenPGP WG made good use of the experimental range in
the OpenPGP Pubkey Algorithms registry over the past year when
developing draft-ietf-openpgp-pqc (just now coming out of WGLC, yay!)

Once development branches of multiple implementations could confirm that
they were able to interoperate using the experimental codepoints for new
algorithms, the specification was adjusted to use non-experimental
codepoints so that the experimental codepoints could be reused for the
next wave of experimentation.  All the participating implementers needed
to do was to adjust one constant in their development branches before
moving to production.

Poppler wants to create interoperable OpenPGP signatures for PDF
documents, right?  If that's the case, I don't think Poppler's proposed
use here falls into either of these two categories.

This seems like an entirely different question to me, because the X-foo
style MIME or HTTP headers represent an open-ended string-based
registry, while the reserved codepoint ranges in OpenPGP are a very
limited fixed set of numeric identifiers (typically constrained to a
single octet; with packet types, constrained to only 6 bits!)

By the way, using an X- header is deprecated in open-ended namespaces,
for a variety of reasons that i won't re-hash in this message, but i do
recommend anyone read if they're interested in helping to evolve
protocols used on the open Internet:

https://www.rfc-editor.org/rfc/rfc6648

At any rate, i don't think questions about the X-header are relevant to
https://bugs.debian.org/1105820.

This isn't how i see it (and by the way, it's not just Debian who has
adopted these patches in the face of upstream delay).

This situation was apparently triggered by poppler, some few months ago,
starting to emit what are ostensibly OpenPGP signatures.  But they
include packets assigned to private or experimental codepoints.  This
suggests that there is no expectation of interoperability with any
OpenPGP implementation, including non-GnuPG implementations, and
potentially any future versions of GnuPG that decide to limit exposure
to their message parser in a detached signature context.

I don't think poppler's choice of private or experimental use packets is
a good idea, as i've noted in
https://gitlab.freedesktop.org/poppler/poppler/-/issues/1595

I don't think this is the same situation at all.

I'd be happy to help you fix poppler to not emit experimental
codepoints, if you would find that useful.  Let's follow up over on
https://gitlab.freedesktop.org/poppler/poppler/-/issues/1595

Regards,

#1105820#50
Date:
2025-05-18 18:02:28 UTC
From:
To:
Before this change in poppler, the GnuPG backend already required a
functioning GnuPG suite setup.

This work in poppler has been done in full cooperation with GnuPG upstream, it
is in the GnuPG backend of poppler and these signatures is created in
g10code's namespace in the pdf files. (g10c.pgp.signature.detached)

The packets used here has, as written, been available since 1998 and given all
of that, I don't expect GnuPG upstream to remove the support for it.

/Sune

#1105820#55
Date:
2025-05-20 20:23:03 UTC
From:
To:
What i'm hearing from this is that poppler wants interoperability with
other GnuPG installations, but not with other OpenPGP installations.

Is that correct?  That doesn't seem like a great strategy for Poppler,
or for the PDF ecosystem.  Why not produce standard padding packets?

Alternately, you could forge a phony signature packet of arbitrary
length, and use that as padding.  it won't verify, but the presence of a
single verified signature should be sufficient.
standard padding packets during detached signature verification:

```
0 dkg@bob:~$ mkdir -m 0700 g
0 dkg@bob:~$ gpg --batch --homedir g --pinentry-mode=loopback --passphrase='' --quick-generate-key tester
gpg: /tmp/cdtemp.3INGsv/g/trustdb.gpg: trustdb created
gpg: directory '/tmp/cdtemp.3INGsv/g/openpgp-revocs.d' created
gpg: revocation certificate stored as '/tmp/cdtemp.3INGsv/g/openpgp-revocs.d/54DA9057C0E2F254FAE89D13266C90CBEFDE69A7.rev'
0 dkg@bob:~$ echo test > test.txt
0 dkg@bob:~$ gpg --batch --homedir g --detach-sign  < test.txt --output test.txt.sig
0 dkg@bob:~$ gpg --list-packets < test.txt.sig
# off=0 ctb=88 tag=2 hlen=2 plen=117
:signature packet: algo 22, keyid 266C90CBEFDE69A7
	version 4, created 1747769160, md5len 0, sigclass 0x00
	digest algo 10, begin of digest f7 4a
	hashed subpkt 33 len 21 (issuer fpr v4 54DA9057C0E2F254FAE89D13266C90CBEFDE69A7)
	hashed subpkt 2 len 4 (sig created 2025-05-20)
	subpkt 16 len 8 (issuer key ID 266C90CBEFDE69A7)
	data: [256 bits]
	data: [256 bits]
0 dkg@bob:~$ cp test.txt.sig test.txt.sig.padded
0 dkg@bob:~$ printf '\325\001\000' >> test.txt.sig.padded
0 dkg@bob:~$ gpg --list-packets < test.txt.sig.padded
# off=0 ctb=88 tag=2 hlen=2 plen=117
:signature packet: algo 22, keyid 266C90CBEFDE69A7
	version 4, created 1747769160, md5len 0, sigclass 0x00
	digest algo 10, begin of digest f7 4a
	hashed subpkt 33 len 21 (issuer fpr v4 54DA9057C0E2F254FAE89D13266C90CBEFDE69A7)
	hashed subpkt 2 len 4 (sig created 2025-05-20)
	subpkt 16 len 8 (issuer key ID 266C90CBEFDE69A7)
	data: [256 bits]
	data: [256 bits]
# off=119 ctb=d5 tag=21 hlen=2 plen=1 new-ctb
:unknown packet: type 21, length 1
dump: 00
0 dkg@bob:~$ gpg --batch --homedir g --status-file status.no-padding --verify test.txt.sig test.txt
gpg: Signature made Tue 20 May 2025 03:26:00 PM EDT
gpg:                using EDDSA key 54DA9057C0E2F254FAE89D13266C90CBEFDE69A7
gpg: checking the trustdb
gpg: marginals needed: 3  completes needed: 1  trust model: pgp
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: next trustdb check due at 2028-05-19
gpg: Good signature from "tester" [ultimate]
0 dkg@bob:~$ gpg --batch --homedir g --status-file status.padding --verify test.txt.sig.padded test.txt
gpg: Signature made Tue 20 May 2025 03:26:00 PM EDT
gpg:                using EDDSA key 54DA9057C0E2F254FAE89D13266C90CBEFDE69A7
gpg: Good signature from "tester" [ultimate]
0 dkg@bob:~$ diff -u status.no-padding status.padding
0 dkg@bob:~$
```

As you can see from the end of this transcript, the status file
descriptor (which is what is parsed by gpgme) produces the exact same
output, whether a padding packet is present or not.

do you expect a future version of GnuPG will deliberately reject a
padding packet?  If so, why?

If not, Poppler has two choices: ⓐ produce a standards-compliant comment
packet, meaning that the OpenPGP signature will interop with any
standards-compliant OpenPGP implementation; or ⓑ produce a
non-standards-compliant padding packet.

It looks to me like stock GnuPG will accept (meaning: ignore) either of
them and still verify.  Choice ⓐ seems obviously preferable when
considering the ecosystem of available OpenPGP verifiers, which are
more likely to consult the standard, and may well be stricter about
unknown/undocumented packets than GnuPG is.

#1105820#60
Date:
2025-05-21 08:32:29 UTC
From:
To:
Upstream released GnuPG is the reference implementation for the pdf bits in
the g10c namespace, and given it can't be done in a good way within rfc-4880,
a GnuPG extension that has existed since 1998 was used.

Unfortunately you seem to be wrong here.

I have added a autotest to poppler that signs with a
g10c.pgp.signature.detached and read back the signature on a document and that
fails with you submitted merge request. I have also tried it manually and got
the same results.

/Sune

#1105820#65
Date:
2025-05-27 08:40:29 UTC
From:
To:
Now that sequoia also thinks that having non-critical packets anywhere, can we
also let GnuPG do it, right ?  right ?

https://gitlab.com/sequoia-pgp/sequoia/-/issues/1193#note_2522532582

/Sune

#1105820#70
Date:
2025-05-27 16:14:53 UTC
From:
To:
An argument from a close read of the specification that multiple
implementers have reviewed and agreed to implement is certainly more
convincing from an interoperability perspective than the argument of
"GnuPG has always done things this way".

It'd be even more compelling if GnuPG upstream would aim at following
the standard in question, but yes, i agree with you that we're trying to
coax the version in Debian to be more standards-compatible than the raw
upstream, to increase the likelihood of functioning interoperability.

However, this still doesn't address the main concern from the patch in
question that you're asking to be modified.  In particular, that patch
tries to minimize the attack surface of arbitrary packet parsers for
material most likely to be coming from an adversary.

I can take a look and see whether it's possible to get both kinds of
benefits -- standards compliance and reduced attack surface -- but i'd
certainly appreciate some upstream support in doing so.

#1105820#75
Date:
2025-05-27 18:34:40 UTC
From:
To:
Did you check the upstream committed patch to deny compressed packets unless
specifically compat-enabled?

https://dev.gnupg.org/rG23ccad05c68005b580c7b209e2242bb93893af62

/Sune

#1105820#80
Date:
2025-05-27 19:19:27 UTC
From:
To:
Yes, i'm happy to see at least this part of the attack surface reduction
being taken seriously by upstream.  I welcome this change in behavior,
where compressed packets are not accepted in an OpenPGP certificate.

I'm less keen on the patch's addition of the "compatibility mode" in
this patch.  Each such mode effectively doubles the scope of any attempt
at comprehensive testing of the GnuPG tooling (you have to run any tests
once without the mode, and once with it), and makes triaging bug reports
more challenging (you have to figure out whether the user was in the
mode or not).  If there are known, unfixable, important uses for a
particular compatibility mode, then i get it.  otherwise, i think
avoiding the complexity is a better option.

I also don't think this patch addresses detached signatures, which are
really the most common place where attacker-controlled data will appear.

I think the most common case for GnuPG use today is where a user already
has a trusted certificate (delivered e.g. by their operating system or
some other trusted channel) and they're verifying the authenticity of a
downloaded piece of infrastructure (e.g. software update, or even an
OpenPGP-signed PDF ☺).  That's typically the detached-signature form of
operation, and the patch you're pointing to doesn't cover it at all.

I don't mind tooling that skips blindly over unexpected non-critical
packets without parsing their internals or trying to handle them: it's
the parsing+handling that is the dangerous part, given the complexity of
the GnuPG codebase.  The packet framing on its own should be
comparatively less dangerous and better tested.

But if we can't avoid parsing+handling, then bailing on unexpected
packets seems like a plausible first-order line of defense.  And I
haven't had the time to figure out how to avoid parsing/handling.  If
you have other insights or suggested patches, i'd be happy to review
them.

Thanks for looking into this further with me.

Regards,