You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
OpenPGP: Normalization when hashing for Text-Mode Signatures #2553
However, the ecosystem-spanning "OpenPGP interoperability test suite" now has new test cases that effectively act as a survey of all implementations.
In this test, PGPainless (based on BC Java, cc @vanitasvitae) exhibits normalization of bare "CR" characters that doesn't interoperate with most other implementations.
FYI: There is a lot of additional context in this email thread in the IETF OpenPGP WG, if anyone is interested.
Though I don't think it's necessary to read all of that, for this ticket 😅
Thanks @hko-s for the report and the interop-suite pointers. This should be in the beta soon.
Text signatures (type 0x01): a CR not followed by LF is now hashed unaltered, so only a bare LF becomes CRLF. That matches GnuPG, GopenPGP, OpenPGP.js, rpgpie and the OpenPGP messages draft. PGPCanonicalizedDataGenerator also no longer turns a bare CR into CRLF in the text literal data it writes, so a signature over the original data still verifies over the literal data.
Cleartext signatures: a line now ends only at LF or CRLF, and a bare CR is part of the text. A dash after a bare CR is no longer dash-escaped on output, and on input it is no longer un-escaped or taken as the start of the signature armor. The output now matches gpg 2.4.4 byte for byte.
Compatibility:
When verifying, a text signature that fails over the new form is also checked against the old CR→CRLF form. That form is what earlier BC releases and Sequoia 2.x produce, so existing signatures keep verifying. Set org.bouncycastle.openpgp.text_sig_allow_legacy_cr=false to accept only the new form.
A cleartext message whose armor itself uses CR-only line endings, such as a classic Mac file, is still read with CR as its line ending. Setting org.bouncycastle.openpgp.csf_bare_cr_line_ending=true reads every cleartext message the old way.
One remaining difference: GnuPG drops CRs right before a line ending or at the end of the data when hashing ("a\r" is hashed as "a"), so those particular GnuPG text signatures still won't verify here. No other implementation in the suite does that, so we haven't copied it.
Thank you for looking into this, and resolving the matter so swiftly!
I did not realize that GnuPG drops CRs before line endings, and not just at the end of the file (if so, that sounds like parts of the cleartext business logic - which is supposed to skip hashing whitespace at the end of lines - might have seeped into GnuPG's "regular" text mode. Curious.)
Your solution sounds sensible to me. As you suggest, not mirroring GnuPG's special handling of CR at the end of file (and lines?) seems like the best approach to me as well. Even though it's of course unfortunate that this means some edge cases will not be interoperable with GnuPG.
Activity
hko-s commentedon Nov 4, 2026
FYI: There is a lot of additional context in this email thread in the IETF OpenPGP WG, if anyone is interested.
Though I don't think it's necessary to read all of that, for this ticket 😅
roy-basmacier commentedon Nov 4, 2026
Thanks @hko-s for the report and the interop-suite pointers. This should be in the beta soon.
Text signatures (type 0x01): a CR not followed by LF is now hashed unaltered, so only a bare LF becomes CRLF. That matches GnuPG, GopenPGP, OpenPGP.js, rpgpie and the OpenPGP messages draft. PGPCanonicalizedDataGenerator also no longer turns a bare CR into CRLF in the text literal data it writes, so a signature over the original data still verifies over the literal data.
Cleartext signatures: a line now ends only at LF or CRLF, and a bare CR is part of the text. A dash after a bare CR is no longer dash-escaped on output, and on input it is no longer un-escaped or taken as the start of the signature armor. The output now matches gpg 2.4.4 byte for byte.
Compatibility:
One remaining difference: GnuPG drops CRs right before a line ending or at the end of the data when hashing ("a\r" is hashed as "a"), so those particular GnuPG text signatures still won't verify here. No other implementation in the suite does that, so we haven't copied it.
hko-s commentedon Nov 4, 2026
Thank you for looking into this, and resolving the matter so swiftly!
I did not realize that GnuPG drops CRs before line endings, and not just at the end of the file (if so, that sounds like parts of the cleartext business logic - which is supposed to skip hashing whitespace at the end of lines - might have seeped into GnuPG's "regular" text mode. Curious.)
Your solution sounds sensible to me. As you suggest, not mirroring GnuPG's special handling of CR at the end of file (and lines?) seems like the best approach to me as well. Even though it's of course unfortunate that this means some edge cases will not be interoperable with GnuPG.
Hash bare CR unaltered in OpenPGP text signatures, keep legacy CR->CR…
Hash OpenPGP text-mode bare CR unaltered and stop treating it as a li…
Hash OpenPGP text-mode bare CR unaltered and stop treating it as a li…