[PATCH 09/10] kyber: use strong random for encapsulation coins
Jussi Kivilinna <[email protected]> Sun, 2 Aug 2026 12:55:14 +0300
| Newsgroups | gmane.comp.encryption.gpg.libgcrypt.devel |
|---|---|
| Message-ID | <[email protected]> |
* cipher/kyber.c (randombytes): Remove.
(crypto_kem_keypair_2, crypto_kem_keypair_3, crypto_kem_keypair_4)
(crypto_kem_enc_2, crypto_kem_enc_3, crypto_kem_enc_4): Remove
declarations.
(crypto_kem_keypair, crypto_kem_enc): Remove variant defines.
(kyber_keypair): Generate coins with GCRY_VERY_STRONG_RANDOM and always
use derandomized entry point.
(kyber_encap): Likewise, but with GCRY_STRONG_RANDOM.
* cipher/kyber-kdep.c (crypto_kem_keypair, crypto_kem_enc): Remove.
--
'randombytes' used GCRY_VERY_STRONG_RANDOM for both key generation and
encapsulation. Encapsulation coins are per message ephemeral value, same
as ECDH ephemeral secret in 'ecc-ecdh.c' and as coins for sntrup761 and
Classic McEliece, all of which use GCRY_STRONG_RANDOM. Level 2 request
forces jitterentropy gathering costing about 2 ms, which is two orders
of magnitude more than the encapsulation itself.
Key generation keeps level 2 as with RSA, DSA and ECC long term keys.
Entry points already had derandomized variants for caller supplied
coins, so glue now uses those and generates coins itself.
Benchmark on AMD Ryzen 9 9950X3D, encapsulation usec/operation, quick
random disabled:
before after speedup
ML-KEM-512 1940.51 19.27 100.70x
ML-KEM-768 1953.88 28.43 68.73x
ML-KEM-1024 1966.06 39.58 49.67x
Signed-off-by: Jussi Kivilinna <[email protected]>
---
cipher/kyber-kdep.c | 49 ------------------------------
cipher/kyber.c | 74 +++++++++++++++++----------------------------
2 files changed, 28 insertions(+), 95 deletions(-)
diff --git a/cipher/kyber-kdep.c b/cipher/kyber-kdep.c
index 774504ce..6d2fe9ed 100644
--- a/cipher/kyber-kdep.c
+++ b/cipher/kyber-kdep.c
@@ -415,28 +415,6 @@ int crypto_kem_keypair_derand(uint8_t *pk,
return 0;
}
-/*************************************************
-* Name: crypto_kem_keypair
-*
-* Description: Generates public and private key
-* for CCA-secure Kyber key encapsulation mechanism
-*
-* Arguments: - uint8_t *pk: pointer to output public key
-* (an already allocated array of KYBER_PUBLICKEYBYTES bytes)
-* - uint8_t *sk: pointer to output private key
-* (an already allocated array of KYBER_SECRETKEYBYTES bytes)
-*
-* Returns 0 (success)
-**************************************************/
-int crypto_kem_keypair(uint8_t *pk,
- uint8_t *sk)
-{
- uint8_t coins[2*KYBER_SYMBYTES];
- randombytes(coins, 2*KYBER_SYMBYTES);
- crypto_kem_keypair_derand(pk, sk, coins);
- return 0;
-}
-
/*************************************************
* Name: crypto_kem_enc_derand
*
@@ -476,31 +454,6 @@ int crypto_kem_enc_derand(uint8_t *ct,
return 0;
}
-/*************************************************
-* Name: crypto_kem_enc
-*
-* Description: Generates cipher text and shared
-* secret for given public key
-*
-* Arguments: - uint8_t *ct: pointer to output cipher text
-* (an already allocated array of KYBER_CIPHERTEXTBYTES bytes)
-* - uint8_t *ss: pointer to output shared secret
-* (an already allocated array of KYBER_SSBYTES bytes)
-* - const uint8_t *pk: pointer to input public key
-* (an already allocated array of KYBER_PUBLICKEYBYTES bytes)
-*
-* Returns 0 (success)
-**************************************************/
-int crypto_kem_enc(uint8_t *ct,
- uint8_t *ss,
- const uint8_t *pk)
-{
- uint8_t coins[KYBER_SYMBYTES];
- randombytes(coins, KYBER_SYMBYTES);
- crypto_kem_enc_derand(ct, ss, pk, coins);
- return 0;
-}
-
/*************************************************
* Name: crypto_kem_dec
*
@@ -803,8 +756,6 @@ void polyvec_add(polyvec *r, const polyvec *a, const polyvec *b)
#undef poly_getnoise_eta1
#undef crypto_kem_keypair_derand
#undef crypto_kem_enc_derand
-#undef crypto_kem_keypair
-#undef crypto_kem_enc
#undef crypto_kem_dec
#undef polyvec
#undef polyvec_compress
diff --git a/cipher/kyber.c b/cipher/kyber.c
index 82d906f1..a8291c26 100644
--- a/cipher/kyber.c
+++ b/cipher/kyber.c
@@ -108,10 +108,6 @@ static int crypto_kem_keypair_derand_3(uint8_t *pk, uint8_t *sk,
static int crypto_kem_keypair_derand_4(uint8_t *pk, uint8_t *sk,
const uint8_t *coins);
-static int crypto_kem_keypair_2(uint8_t *pk, uint8_t *sk);
-static int crypto_kem_keypair_3(uint8_t *pk, uint8_t *sk);
-static int crypto_kem_keypair_4(uint8_t *pk, uint8_t *sk);
-
static int crypto_kem_enc_derand_2(uint8_t *ct, uint8_t *ss, const uint8_t *pk,
const uint8_t *coins);
static int crypto_kem_enc_derand_3(uint8_t *ct, uint8_t *ss, const uint8_t *pk,
@@ -119,10 +115,6 @@ static int crypto_kem_enc_derand_3(uint8_t *ct, uint8_t *ss, const uint8_t *pk,
static int crypto_kem_enc_derand_4(uint8_t *ct, uint8_t *ss, const uint8_t *pk,
const uint8_t *coins);
-static int crypto_kem_enc_2(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
-static int crypto_kem_enc_3(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
-static int crypto_kem_enc_4(uint8_t *ct, uint8_t *ss, const uint8_t *pk);
-
static int crypto_kem_dec_2(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
static int crypto_kem_dec_3(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
static int crypto_kem_dec_4(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
@@ -130,56 +122,60 @@ static int crypto_kem_dec_4(uint8_t *ss, const uint8_t *ct, const uint8_t *sk);
void
kyber_keypair (int algo, uint8_t *pk, uint8_t *sk, const uint8_t *coins)
{
+ uint8_t rnd[GCRY_KEM_MLKEM_RANDOM_LEN * 2];
+
+ if (!coins)
+ {
+ /* Long term key material. */
+ _gcry_randomize (rnd, sizeof (rnd), GCRY_VERY_STRONG_RANDOM);
+ coins = rnd;
+ }
+
switch (algo)
{
case GCRY_KEM_MLKEM512:
- if (coins)
- crypto_kem_keypair_derand_2 (pk, sk, coins);
- else
- crypto_kem_keypair_2 (pk, sk);
+ crypto_kem_keypair_derand_2 (pk, sk, coins);
break;
case GCRY_KEM_MLKEM768:
default:
- if (coins)
- crypto_kem_keypair_derand_3 (pk, sk, coins);
- else
- crypto_kem_keypair_3 (pk, sk);
+ crypto_kem_keypair_derand_3 (pk, sk, coins);
break;
case GCRY_KEM_MLKEM1024:
- if (coins)
- crypto_kem_keypair_derand_4 (pk, sk, coins);
- else
- crypto_kem_keypair_4 (pk, sk);
+ crypto_kem_keypair_derand_4 (pk, sk, coins);
break;
}
+
+ wipememory (rnd, sizeof (rnd));
}
void
kyber_encap (int algo, uint8_t *ct, uint8_t *ss, const uint8_t *pk,
const uint8_t *coins)
{
+ uint8_t rnd[GCRY_KEM_MLKEM_RANDOM_LEN];
+
+ if (!coins)
+ {
+ /* Per message value, same level as ECDH ephemeral secret. */
+ _gcry_randomize (rnd, sizeof (rnd), GCRY_STRONG_RANDOM);
+ coins = rnd;
+ }
+
switch (algo)
{
case GCRY_KEM_MLKEM512:
- if (coins)
- crypto_kem_enc_derand_2 (ct, ss, pk, coins);
- else
- crypto_kem_enc_2 (ct, ss, pk);
+ crypto_kem_enc_derand_2 (ct, ss, pk, coins);
break;
case GCRY_KEM_MLKEM768:
default:
- if (coins)
- crypto_kem_enc_derand_3 (ct, ss, pk, coins);
- else
- crypto_kem_enc_3 (ct, ss, pk);
+ crypto_kem_enc_derand_3 (ct, ss, pk, coins);
break;
case GCRY_KEM_MLKEM1024:
- if (coins)
- crypto_kem_enc_derand_4 (ct, ss, pk, coins);
- else
- crypto_kem_enc_4 (ct, ss, pk);
+ crypto_kem_enc_derand_4 (ct, ss, pk, coins);
break;
}
+
+ wipememory (rnd, sizeof (rnd));
}
void
@@ -200,12 +196,6 @@ kyber_decap (int algo, uint8_t *ss, const uint8_t *ct, const uint8_t *sk)
}
}
-static void
-randombytes (uint8_t *out, size_t outlen)
-{
- _gcry_randomize (out, outlen, GCRY_VERY_STRONG_RANDOM);
-}
-
typedef struct {
gcry_md_hd_t h;
} keccak_state;
@@ -290,8 +280,6 @@ sha3_512 (uint8_t h[64], const uint8_t *in, size_t inlen)
#else
#include "kyber.h"
-void randombytes (uint8_t *out, size_t outlen);
-
typedef struct {
uint64_t s[25];
unsigned int pos;
@@ -472,8 +460,6 @@ static void kyber_shake128_absorb (keccak_state *state,
# define poly_getnoise_eta1 poly_getnoise_eta1_2
# define crypto_kem_keypair_derand VARIANT2(crypto_kem_keypair_derand)
# define crypto_kem_enc_derand VARIANT2(crypto_kem_enc_derand)
-# define crypto_kem_keypair VARIANT2(crypto_kem_keypair)
-# define crypto_kem_enc VARIANT2(crypto_kem_enc)
# define crypto_kem_dec VARIANT2(crypto_kem_dec)
# define polyvec VARIANT2(polyvec)
# define polyvec_compress VARIANT2(polyvec_compress)
@@ -505,8 +491,6 @@ static void kyber_shake128_absorb (keccak_state *state,
# define poly_getnoise_eta1 poly_getnoise_eta1_3_4
# define crypto_kem_keypair_derand VARIANT3(crypto_kem_keypair_derand)
# define crypto_kem_enc_derand VARIANT3(crypto_kem_enc_derand)
-# define crypto_kem_keypair VARIANT3(crypto_kem_keypair)
-# define crypto_kem_enc VARIANT3(crypto_kem_enc)
# define crypto_kem_dec VARIANT3(crypto_kem_dec)
# define polyvec VARIANT3(polyvec)
# define polyvec_compress VARIANT3(polyvec_compress)
@@ -538,8 +522,6 @@ static void kyber_shake128_absorb (keccak_state *state,
# define poly_getnoise_eta1 poly_getnoise_eta1_3_4
# define crypto_kem_keypair_derand VARIANT4(crypto_kem_keypair_derand)
# define crypto_kem_enc_derand VARIANT4(crypto_kem_enc_derand)
-# define crypto_kem_keypair VARIANT4(crypto_kem_keypair)
-# define crypto_kem_enc VARIANT4(crypto_kem_enc)
# define crypto_kem_dec VARIANT4(crypto_kem_dec)
# define polyvec VARIANT4(polyvec)
# define polyvec_compress VARIANT4(polyvec_compress)
--
2.53.0