[PATCH 10/10] dilithium: use strong random for signature nonce

Jussi Kivilinna <[email protected]> Sun, 2 Aug 2026 12:55:15 +0300
Newsgroups gmane.comp.encryption.gpg.libgcrypt.devel
Message-ID <[email protected]>
* cipher/pubkey-dilithium.c (randombytes): Add 'level' parameter.
(mldsa_generate): Use GCRY_VERY_STRONG_RANDOM for key seed.
(mldsa_sign): Use GCRY_STRONG_RANDOM for per signature value.
--

'randombytes' used GCRY_VERY_STRONG_RANDOM for both key seed and hedged
signing nonce. Nonce is per signature ephemeral value, same as ECDSA,
DSA and GOST nonces, all of which use GCRY_STRONG_RANDOM. Level 2
request forces jitterentropy gathering costing about 2 ms, which
dominated signing time. Key seed keeps level 2.

Benchmark on AMD Ryzen 9 9950X3D, signing usec/operation, quick random
disabled:

                 before    after   speedup
 ML-DSA-44      2108.52   140.37    15.02x
 ML-DSA-65      2144.04   223.50     9.59x
 ML-DSA-87      2234.45   272.85     8.19x

Signed-off-by: Jussi Kivilinna <[email protected]>
---
 cipher/pubkey-dilithium.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/cipher/pubkey-dilithium.c b/cipher/pubkey-dilithium.c
index 9d13aad3..92c64d91 100644
--- a/cipher/pubkey-dilithium.c
+++ b/cipher/pubkey-dilithium.c
@@ -121,9 +121,10 @@ mldsa_compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
 
 
 static void
-randombytes (unsigned char *out, size_t outlen)
+randombytes (unsigned char *out, size_t outlen,
+             enum gcry_random_level level)
 {
-  _gcry_randomize (out, outlen, GCRY_VERY_STRONG_RANDOM);
+  _gcry_randomize (out, outlen, level);
 }
 
 static gcry_err_code_t
@@ -151,7 +152,8 @@ mldsa_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
   rc = sexp_extract_param (genparms, NULL, "/S", &seed_mpi, NULL);
   if (rc == GPG_ERR_NOT_FOUND)
     {
-      randombytes (seed, SEEDBYTES);
+      /* Long term key material. */
+      randombytes (seed, SEEDBYTES, GCRY_VERY_STRONG_RANDOM);
       rc = 0;
     }
   else if (rc)
@@ -255,7 +257,10 @@ mldsa_sign (gcry_sexp_t *r_sig, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       memcpy (rnd, ctx.rnd, RNDBYTES);
     }
   else
-    randombytes (rnd, RNDBYTES);
+    {
+      /* Per signature value, same level as ECDSA nonce. */
+      randombytes (rnd, RNDBYTES, GCRY_STRONG_RANDOM);
+    }
   if (ctx.flags & PUBKEY_FLAG_NO_PREFIX)
     rc = dilithium_sign (info->algo, sig, info->sig_len, data, data_len,
                          NULL, -1, sk, rnd);
-- 
2.53.0