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