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
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
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
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!
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.
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
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,
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
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,
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
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.
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
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
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.
Did you check the upstream committed patch to deny compressed packets unless specifically compat-enabled? https://dev.gnupg.org/rG23ccad05c68005b580c7b209e2242bb93893af62 /Sune
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,