EdDSA Verification Bug - Clarification on Format 2 Verification Failure
Zachary Fogg
zach.fogg at gmail.com
Thu Jan 15 17:20:12 CET 2026
thanks for your time. you say you don't see the bug, but did you run my
test programs? they show the bug.
#include <stdio.h>
#include <string.h>
#include <gcrypt.h>
int main(void) {
gcry_error_t err;
gcry_sexp_t keypair = NULL, privkey = NULL, pubkey = NULL;
gcry_sexp_t s_sig = NULL, s_data = NULL;
gcry_check_version(NULL);
gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
// Generate Ed25519 keypair
err = gcry_sexp_build(&keypair, NULL, "(genkey (ecc (curve Ed25519)))");
gcry_pk_genkey(&keypair, keypair);
privkey = gcry_sexp_find_token(keypair, "private-key", 0);
pubkey = gcry_sexp_find_token(keypair, "public-key", 0);
const char *msg = "Test message";
size_t msg_len = strlen(msg);
// TEST 1: GPG agent Format 2 (THE BUG)
printf("Format 2: (data (flags eddsa) (hash-algo sha512) (value
%%b))\n");
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);
printf(" Sign: %s\n", err ? "FAILED" : "OK");
err = gcry_pk_verify(s_sig, s_data, pubkey);
printf(" Verify: %s\n", err ? "FAILED ❌" : "OK");
gcry_sexp_release(s_sig);
gcry_sexp_release(s_data);
// TEST 2: Simple Format 3 (CONTROL - should work)
printf("\nFormat 3: (data (value %%b))\n");
err = gcry_sexp_build(&s_data, NULL, "(data (value %b))", msg_len, msg);
err = gcry_pk_sign(&s_sig, s_data, privkey);
printf(" Sign: %s\n", err ? "FAILED" : "OK");
err = gcry_pk_verify(s_sig, s_data, pubkey);
printf(" Verify: %s\n", err ? "FAILED" : "OK ✓");
// Cleanup
gcry_sexp_release(s_sig);
gcry_sexp_release(s_data);
gcry_sexp_release(pubkey);
gcry_sexp_release(privkey);
gcry_sexp_release(keypair);
return 0;
}
the full issue has more example programs and information why integrating
with libsodium fails in this way.
https://github.com/zfogg/ascii-chat/issues/92
i did however find a work around: have gpg do verification through
gpg-agent. so my ascii-chat program works now, but not via the
libsodium+gcrypt verification method that i originally planned.
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/20260115/555fa739/attachment-0001.html>
More information about the Gcrypt-devel
mailing list