diff mbox series

[wrynose,2/2] libgcrypt: fix CVE-2026-41990

Message ID 20260723123155.795532-3-leonid.iziumtsev@est.tech
State New
Headers show
Series libgcrypt: fix multiple CVEs | expand

Commit Message

Leonid Iziumtsev July 23, 2026, 12:31 p.m. UTC
Backport patch to fix CVE-2026-41990.

References:
  https://nvd.nist.gov/vuln/detail/CVE-2026-41990
  https://osv.dev/list?q=CVE-2026-41990

Upstream fix:
  https://github.com/gpg/libgcrypt/commit/905e00f046a71e5670517779afaf85a354952832

Signed-off-by: Leonid Iziumtsev <leonid.iziumtsev@est.tech>
---
 .../libgcrypt/files/CVE-2026-41990.patch      | 316 ++++++++++++++++++
 .../libgcrypt/libgcrypt_1.12.1.bb             |   1 +
 2 files changed, 317 insertions(+)
 create mode 100644 meta/recipes-support/libgcrypt/files/CVE-2026-41990.patch
diff mbox series

Patch

diff --git a/meta/recipes-support/libgcrypt/files/CVE-2026-41990.patch b/meta/recipes-support/libgcrypt/files/CVE-2026-41990.patch
new file mode 100644
index 0000000000..a4f1809227
--- /dev/null
+++ b/meta/recipes-support/libgcrypt/files/CVE-2026-41990.patch
@@ -0,0 +1,316 @@ 
+From 024391fc505ebf9c47e51953d7d85995ba179576 Mon Sep 17 00:00:00 2001
+From: NIIBE Yutaka <gniibe@fsij.org>
+Date: Mon, 13 Apr 2026 11:51:46 +0900
+Subject: [PATCH] cipher:dilithium: Check the label length by caller.
+
+* cipher/dilithium.h (dilithium_keypair, dilithium_sign)
+(dilithium_verify): Return gpg_err_code_t.
+* cipher/dilithium.c (dilithium_keypair): Return gpg_err_code_t.
+(dilithium_sign, dilithium_verify): Ditto.  Check CTXLEN.
+* cipher/pubkey-dilithium.c (mldsa_generate): Follow the change.
+(mldsa_sign, mldsa_verify): Likewise.
+
+--
+
+Reported by Calif.io in collaboration with Claude and Anthropic
+Research.
+
+GnuPG-bug-id: 8208
+Co-authored-by: Thai Duong <thai@calif.io>
+Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
+
+CVE: CVE-2026-41990
+Upstream-Status: Backport [https://github.com/gpg/libgcrypt/commit/905e00f046a71e5670517779afaf85a354952832]
+
+Signed-off-by: Leonid Iziumtsev <leonid.iziumtsev@est.tech>
+---
+ cipher/dilithium.c        | 101 ++++++++++++++++++++++++++++----------
+ cipher/dilithium.h        |  20 ++++----
+ cipher/pubkey-dilithium.c |  34 +++++--------
+ 3 files changed, 99 insertions(+), 56 deletions(-)
+
+diff --git a/cipher/dilithium.c b/cipher/dilithium.c
+index 955feb2a..212c4afe 100644
+--- a/cipher/dilithium.c
++++ b/cipher/dilithium.c
+@@ -82,6 +82,7 @@
+ #include "gcrypt-int.h"
+ #include "const-time.h"
+ 
++/* With glue code, we only use the "_internal" API of Dilithium.  */
+ #define DILITHIUM_INTERNAL_API_ONLY 1
+ 
+ #include "dilithium.h"
+@@ -120,23 +121,33 @@ static int crypto_sign_verify_internal_5 (const uint8_t *sig, size_t siglen,
+                                           const uint8_t *pre, size_t prelen,
+                                           const uint8_t *pk);
+ 
+-int
++gpg_err_code_t
+ dilithium_keypair (int algo, uint8_t *pk, uint8_t *sk,
+                    const uint8_t seed[SEEDBYTES])
+ {
++  int r;
++
+   switch (algo)
+     {
+     case GCRY_MLDSA44:
+-      return crypto_sign_keypair_internal_2 (pk, sk, seed);
++      r = crypto_sign_keypair_internal_2 (pk, sk, seed);
++      break;
+     case GCRY_MLDSA65:
+     default:
+-      return crypto_sign_keypair_internal_3 (pk, sk, seed);
++      r = crypto_sign_keypair_internal_3 (pk, sk, seed);
++      break;
+     case GCRY_MLDSA87:
+-      return crypto_sign_keypair_internal_5 (pk, sk, seed);
++      r = crypto_sign_keypair_internal_5 (pk, sk, seed);
++      break;
+     }
++
++  if (r < 0)
++    return GPG_ERR_INTERNAL;
++
++  return 0;
+ }
+ 
+-int
++gpg_err_code_t
+ dilithium_sign (int algo, uint8_t *sig, size_t siglen,
+                 const uint8_t *m, size_t mlen,
+                 const uint8_t *ctx, size_t ctxlen,
+@@ -145,9 +156,17 @@ dilithium_sign (int algo, uint8_t *sig, size_t siglen,
+   size_t i;
+   uint8_t pre[257];
+   size_t prelen;
++  int r;
+ 
+-  if (ctx == NULL && ctxlen == -1)
+-    prelen = 0;
++  if (ctx == NULL)
++    {
++      if (ctxlen == -1)
++        prelen = 0;
++      else
++        return GPG_ERR_INV_DATA;
++    }
++  else if (ctxlen > 255)
++    return GPG_ERR_INV_DATA;
+   else
+     {
+       /* Prepare pre = (0, ctxlen, ctx) */
+@@ -158,28 +177,44 @@ dilithium_sign (int algo, uint8_t *sig, size_t siglen,
+       prelen = 2 + ctxlen;
+     }
+ 
++  /*
++   * Note that the second argument of the upstream routine is the
++   * pointer to output length of signature.  It assumes the first
++   * argument (pointer to output signature) should have correct (or
++   * more) length, beforehand.
++   *
++   * Before calling the routine, we should check the length.
++   */
+   switch (algo)
+     {
+     case GCRY_MLDSA44:
+       if (siglen != CRYPTO_BYTES_2)
+-        return -1;
+-      return crypto_sign_signature_internal_2 (sig, &siglen, m, mlen,
+-                                               pre, prelen, rnd, sk);
++        return GPG_ERR_INV_DATA;
++      r = crypto_sign_signature_internal_2 (sig, &siglen, m, mlen,
++                                            pre, prelen, rnd, sk);
++      break;
+     case GCRY_MLDSA65:
+     default:
+       if (siglen != CRYPTO_BYTES_3)
+-        return -1;
+-      return crypto_sign_signature_internal_3 (sig, &siglen, m, mlen,
+-                                               pre, prelen, rnd, sk);
++        return GPG_ERR_INV_DATA;
++      r = crypto_sign_signature_internal_3 (sig, &siglen, m, mlen,
++                                            pre, prelen, rnd, sk);
++      break;
+     case GCRY_MLDSA87:
+       if (siglen != CRYPTO_BYTES_5)
+-        return -1;
+-      return crypto_sign_signature_internal_5 (sig, &siglen, m, mlen,
+-                                               pre, prelen, rnd, sk);
++        return GPG_ERR_INV_DATA;
++      r = crypto_sign_signature_internal_5 (sig, &siglen, m, mlen,
++                                            pre, prelen, rnd, sk);
++      break;
+     }
++
++  if (r < 0)
++    return GPG_ERR_INTERNAL;
++
++  return 0;
+ }
+ 
+-int
++gpg_err_code_t
+ dilithium_verify (int algo, const uint8_t *sig, size_t siglen,
+                   const uint8_t *m, size_t mlen,
+                   const uint8_t *ctx, size_t ctxlen,
+@@ -188,9 +223,17 @@ dilithium_verify (int algo, const uint8_t *sig, size_t siglen,
+   size_t i;
+   uint8_t pre[257];
+   size_t prelen;
++  int r;
+ 
+-  if (ctx == NULL && ctxlen == -1)
+-    prelen = 0;
++  if (ctx == NULL)
++    {
++      if (ctxlen == -1)
++        prelen = 0;
++      else
++        return GPG_ERR_INV_DATA;
++    }
++  else if (ctxlen > 255)
++    return GPG_ERR_INV_DATA;
+   else
+     {
+       /* Prepare pre = (0, ctxlen, ctx) */
+@@ -204,16 +247,24 @@ dilithium_verify (int algo, const uint8_t *sig, size_t siglen,
+   switch (algo)
+     {
+     case GCRY_MLDSA44:
+-      return crypto_sign_verify_internal_2 (sig, siglen, m, mlen,
+-                                            pre, prelen, pk);
++      r = crypto_sign_verify_internal_2 (sig, siglen, m, mlen,
++                                         pre, prelen, pk);
++      break;
+     case GCRY_MLDSA65:
+     default:
+-      return crypto_sign_verify_internal_3 (sig, siglen, m, mlen,
+-                                            pre, prelen, pk);
++      r = crypto_sign_verify_internal_3 (sig, siglen, m, mlen,
++                                         pre, prelen, pk);
++      break;
+     case GCRY_MLDSA87:
+-      return crypto_sign_verify_internal_5 (sig, siglen, m, mlen,
+-                                            pre, prelen, pk);
++      r = crypto_sign_verify_internal_5 (sig, siglen, m, mlen,
++                                         pre, prelen, pk);
++      break;
+     }
++
++  if (r < 0)
++    return GPG_ERR_BAD_SIGNATURE;
++
++  return 0;
+ }
+ 
+ typedef struct {
+diff --git a/cipher/dilithium.h b/cipher/dilithium.h
+index 88a48094..dc18f158 100644
+--- a/cipher/dilithium.h
++++ b/cipher/dilithium.h
+@@ -64,16 +64,16 @@
+ #define DILITHIUM_SIGN_STACK_BURN (161 * 1024)
+ #define DILITHIUM_VERIFY_STACK_BURN (122 * 1024)
+ 
+-int dilithium_keypair (int algo, uint8_t *pk, uint8_t *sk,
+-                       const uint8_t seed[SEEDBYTES]);
+-int dilithium_sign (int algo, uint8_t *sig, size_t siglen,
+-                    const uint8_t *m, size_t mlen,
+-                    const uint8_t *ctx, size_t ctxlen,
+-                    const uint8_t *sk, const uint8_t rnd[RNDBYTES]);
+-int dilithium_verify (int algo, const uint8_t *sig, size_t siglen,
+-                      const uint8_t *m, size_t mlen,
+-                      const uint8_t *ctx, size_t ctxlen,
+-                      const uint8_t *pk);
++gpg_err_code_t dilithium_keypair (int algo, uint8_t *pk, uint8_t *sk,
++                                  const uint8_t seed[SEEDBYTES]);
++gpg_err_code_t dilithium_sign (int algo, uint8_t *sig, size_t siglen,
++                               const uint8_t *m, size_t mlen,
++                               const uint8_t *ctx, size_t ctxlen,
++                               const uint8_t *sk, const uint8_t rnd[RNDBYTES]);
++gpg_err_code_t dilithium_verify (int algo, const uint8_t *sig, size_t siglen,
++                                 const uint8_t *m, size_t mlen,
++                                 const uint8_t *ctx, size_t ctxlen,
++                                 const uint8_t *pk);
+ #endif
+ 
+ #if defined(DILITHIUM_MODE)
+diff --git a/cipher/pubkey-dilithium.c b/cipher/pubkey-dilithium.c
+index 03958bb0..8c3f650e 100644
+--- a/cipher/pubkey-dilithium.c
++++ b/cipher/pubkey-dilithium.c
+@@ -170,7 +170,7 @@ mldsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
+       memcpy (seed, seed_supplied, SEEDBYTES);
+     }
+ 
+-  dilithium_keypair (info->algo, pk, sk, seed);
++  rc = dilithium_keypair (info->algo, pk, sk, seed);
+   _gcry_burn_stack (DILITHIUM_KEYPAIR_STACK_BURN);
+ 
+   if (!rc)
+@@ -206,7 +206,6 @@ mldsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+   size_t data_len;
+   const unsigned char *sk;
+   const struct mldsa_info *info = mldsa_get_info (keyparms);
+-  int r;
+ 
+   if (!info)
+     return GPG_ERR_PUBKEY_ALGO;
+@@ -258,17 +257,14 @@ mldsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+   else
+     randombytes (rnd, RNDBYTES);
+   if (ctx.flags & PUBKEY_FLAG_NO_PREFIX)
+-    r = dilithium_sign (info->algo, sig, info->sig_len, data, data_len,
+-                        NULL, -1, sk, rnd);
++    rc = dilithium_sign (info->algo, sig, info->sig_len, data, data_len,
++                         NULL, -1, sk, rnd);
+   else
+-    r = dilithium_sign (info->algo, sig, info->sig_len, data, data_len,
+-                        ctx.label, ctx.labellen, sk, rnd);
++    rc = dilithium_sign (info->algo, sig, info->sig_len, data, data_len,
++                         ctx.label, ctx.labellen, sk, rnd);
+   _gcry_burn_stack (DILITHIUM_SIGN_STACK_BURN);
+-  if (r < 0)
+-    {
+-      rc = GPG_ERR_INTERNAL;
+-      goto leave;
+-    }
++  if (rc)
++    goto leave;
+ 
+   rc = sexp_build (r_sig, NULL, "(sig-val(%s(s%b)))", info->name,
+                    info->sig_len, sig);
+@@ -300,7 +296,6 @@ mldsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+   size_t data_len;
+   const unsigned char *pk;
+   const struct mldsa_info *info = mldsa_get_info (keyparms);
+-  int r;
+ 
+   if (!info)
+     return GPG_ERR_PUBKEY_ALGO;
+@@ -350,17 +345,14 @@ mldsa_verify (gcry_sexp_t s_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
+     }
+ 
+   if (ctx.flags & PUBKEY_FLAG_NO_PREFIX)
+-    r = dilithium_verify (info->algo, sig, info->sig_len, data, data_len,
+-                          NULL, -1, pk);
++    rc = dilithium_verify (info->algo, sig, info->sig_len, data, data_len,
++                           NULL, -1, pk);
+   else
+-    r = dilithium_verify (info->algo, sig, info->sig_len, data, data_len,
+-                          ctx.label, ctx.labellen, pk);
++    rc = dilithium_verify (info->algo, sig, info->sig_len, data, data_len,
++                           ctx.label, ctx.labellen, pk);
+   _gcry_burn_stack (DILITHIUM_VERIFY_STACK_BURN);
+-  if (r < 0)
+-    {
+-      rc = GPG_ERR_BAD_SIGNATURE;
+-      goto leave;
+-    }
++  if (rc)
++    goto leave;
+ 
+ leave:
+   _gcry_pk_util_free_encoding_ctx (&ctx);
diff --git a/meta/recipes-support/libgcrypt/libgcrypt_1.12.1.bb b/meta/recipes-support/libgcrypt/libgcrypt_1.12.1.bb
index 55872a5c23..d7f8563ae6 100644
--- a/meta/recipes-support/libgcrypt/libgcrypt_1.12.1.bb
+++ b/meta/recipes-support/libgcrypt/libgcrypt_1.12.1.bb
@@ -26,6 +26,7 @@  SRC_URI = "${GNUPG_MIRROR}/libgcrypt/libgcrypt-${PV}.tar.bz2 \
            file://no-bench-slope.patch \
            file://run-ptest \
            file://CVE-2026-41989.patch \
+           file://CVE-2026-41990.patch \
            "
 SRC_URI[sha256sum] = "7df5c08d952ba33f9b6bdabdb06a61a78b2cf62d2122c2d1d03a91a79832aa3c"