EdDSA Verification Bug - Clarification on Format 2 Verification Failure
Zachary Fogg
zach.fogg at gmail.com
Thu Feb 5 01:23:01 CET 2026
Hey Werner,
I appreciate the feedback, but I have to respectfully push back here. I
don't think this is just an API inconsistency issue.
I set up a minimal test case to verify what's happening, and it's pretty
clear cut:
// Format 2: (data (flags eddsa) (hash-algo sha512) (value %b))
err = gcry_sexp_build(&s_data, NULL,
"(data (flags eddsa) (hash-algo sha512) (value %b))",
msg_len, msg);
err = gcry_pk_sign(&s_sig, s_data, privkey); // OK - signing works
err = gcry_pk_verify(s_sig, s_data, pubkey); // FAILED - verification fails
The thing is, I'm using the exact same S-expression for both signing and
verification. If the format is valid enough for signing to succeed, it
should be valid for verification. That's the inconsistency. You can't have
signing work and verification fail with identical inputs.
I get that Ed25519 has been working well since 2014 - the simple format
(data (value %b)) works fine for me too. But when you add the EdDSA flags
and hash-algo spec (which is what GPG uses), the verification breaks.
That's a real problem.
This isn't some edge case either. I'm trying to verify signatures and it
fails every time because libgcrypt can sign but can't verify using the same
format. For my use case, that's a showstopper for using the lib directly...
I resorted to having the gpg binary do it in its own process which is not
ideal code at all.
I'm not trying to be difficult here - I just want this fixed or at least
documented as a known limitation, and to have tests updated because in my
research this bug exists because there are no tests for specifically this
case. If EdDSA with hash-algo flags isn't supposed to be supported, the
library should reject it at sign time, not silently accept it and then fail
verification.
What do you think? Did you run my example code at least and SEE the
failures in your own terminal? It's reproducible, and I could show you if
you were sitting in front of me.
-Zachary
On Thu, Jan 15, 2026 at 8:41 AM Werner Koch <wk at gnupg.org> wrote:
> Hi!
>
> Just a short note on your bug report. You gave a lot of examples and a
> nicely formated report at https://github.com/zfogg/ascii-chat/issues/92
> but I can't read everything of it.
>
> On Tue, 30 Dec 2025 02:30, Zachary Fogg said:
> > **In-Reply-To:** <response from NIIBE Yutaka on Oct 22, 2025>
>
> > Your response mentioned using `(flags eddsa)` during key generation,
> which
> > is good practice. However, I want to clarify that **my bug report
> concerns
> > signature verification, not key generation**.
>
> If you look at the way GnuPG uses Libgcrypt will find in
> gnupg/g10/pkglue.c:pk_verify this:
>
> if (openpgp_oid_is_ed25519 (pkey[0]))
> fmt = "(public-key(ecc(curve %s)(flags eddsa)(q%m)))";
> else
> fmt = "(public-key(ecc(curve %s)(q%m)))";
>
> and this for the data:
>
> if (openpgp_oid_is_ed25519 (pkey[0]))
> fmt = "(data(flags eddsa)(hash-algo sha512)(value %m))";
> else
> fmt = "(data(value %m))";
>
> and more complicated stuff for re-formatting the signature data. It is
> a bit unfortunate that we need to have these special cases but that's
> the drawback of a having a stable API and protocol.
>
> > 1. Can you confirm this is a genuine bug in libgcrypt's verification
> logic?
>
> No, at least not as I understand it. ed25519 signatures are working
> well and are in active use since GnuPG 2.1 from 2014.
>
> > 2. Should I open a formal bug in the dev.gnupg.org tracker?
>
> I don't see a bug ;-)
>
> > 3. Would a patch fixing the PUBKEY_FLAG_PREHASH handling be acceptable?
>
> I do not understand exactly what you propose. A more concise
> description would be helpful. But note that API stability is a primary
> goal.
>
> BTW on your website your wrote:
>
> I've created a working exploit that demonstrates the severity of this
> bug. The exploit proves that GPG agent creates EdDSA signatures that
> cannot be verified by standard libgcrypt verification code, even with
> the correct keys.
>
> The term "exploit" is used to describe an attack method which undermines
> the security of a system. What you describe is a claimed inconsistent
> API. That may or may not be the case; I don't see a security bug here,
> though.
>
>
>
> Salam-Shalom,
>
> Werner
>
>
> p.s.
> I had a brief look at your project: In src/main.c I notice
>
> // Set global FPS from command-line option if provided
> extern int g_max_fps;
>
> The declaration of an external variable inside a function is a not a
> good coding style. Put this at the top of the file or into a header.
> A few lines above:
>
> #ifndef NDEBUG
> // Initialize lock debugging system after logging is fully set up
> log_debug("Initializing lock debug system...");
>
> Never ever use NDEBUG. This is an idea of the 70ies. This also
> disables the assert(3) functionality and if you do this you won't get an
> assertion failure at all in your production code - either you know the
> code is correct or you are not sure. Never remove an assert from
> production code.
>
> I have noticed a lot of documentation inside the code - that's good.
>
> --
> The pioneers of a warless world are the youth that
> refuse military service. - A. Einstein
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.gnupg.org/pipermail/gcrypt-devel/attachments/20260204/187c0a3f/attachment.html>
More information about the Gcrypt-devel
mailing list