[PATCH] cipher:kem: Validate input length.

NIIBE Yutaka via Gcrypt-devel <[email protected]> Thu, 4 Jun 2026 14:46:45 +0900
Newsgroups gmane.comp.encryption.gpg.libgcrypt.devel
Message-ID <db5ae94eb01feb3cb60d42e6caa75106e3e781fc.1780551989.git.gniibe@fsij.org>
This is a multi-part message in MIME format.
--------------2.47.3
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


* cipher/kem.c (_gcry_kem_genkey): Add input length validation.
(_gcry_kem_encap, _gcry_kem_decap): Likewise.
* src/gcrypt.h.in: Add forgotten constants for X448.

--

Reported-by: Ciwan Ă–ztopal
GnuPG-bug-id: 8259
Signed-off-by: NIIBE Yutaka <[email protected]>
---
 cipher/kem.c    | 124 ++++++++++++++++++++++++++++++++++++++++--------
 src/gcrypt.h.in |  13 +++++
 2 files changed, 118 insertions(+), 19 deletions(-)


--------------2.47.3
Content-Type: text/x-patch; name="0001-cipher-kem-Validate-input-length.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-cipher-kem-Validate-input-length.patch"

diff --git a/cipher/kem.c b/cipher/kem.c
index 2a432a0a..e8fb826e 100644
--- a/cipher/kem.c
+++ b/cipher/kem.c
@@ -95,11 +95,18 @@ _gcry_kem_genkey (int algo,
       if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN
           || pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN)
         return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       sntrup761_keypair (pubkey, seckey, NULL, sntrup761_random);
       _gcry_burn_stack (SNTRUP761_KEYPAIR_STACK_BURN);
       return 0;
 
     case GCRY_KEM_CM6688128F:
+      if (seckey_len != GCRY_KEM_CM6688128F_SECKEY_LEN
+          || pubkey_len != GCRY_KEM_CM6688128F_PUBKEY_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       mceliece6688128f_keypair (pubkey, seckey);
       _gcry_burn_stack (MCELIECE6688128F_KEYPAIR_STACK_BURN);
       return 0;
@@ -164,29 +171,57 @@ _gcry_kem_encap (int algo,
   switch (algo)
     {
     case GCRY_KEM_SNTRUP761:
-      if (optional != NULL || optional_len != 0)
-        return GPG_ERR_INV_VALUE;
       if (pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN
           || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN
           || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN)
-        return GPG_ERR_INV_VALUE;
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       sntrup761_enc (ciphertext, shared, pubkey, NULL, sntrup761_random);
       _gcry_burn_stack (SNTRUP761_ENC_STACK_BURN);
       return 0;
 
     case GCRY_KEM_CM6688128F:
-      if (optional != NULL)
-	return GPG_ERR_INV_VALUE;
+      if (pubkey_len != GCRY_KEM_CM6688128F_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_CM6688128F_ENCAPS_LEN
+          || shared_len != GCRY_KEM_CM6688128F_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       mceliece6688128f_enc (ciphertext, shared, pubkey);
       _gcry_burn_stack (MCELIECE6688128F_ENC_STACK_BURN);
       return 0;
 
 #if USE_KYBER
     case GCRY_KEM_MLKEM512:
+      if (pubkey_len != GCRY_KEM_MLKEM512_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM512_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM512_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN)
+	return GPG_ERR_INV_ARG;
+      kyber_encap (algo, ciphertext, shared, pubkey, optional);
+      _gcry_burn_stack (KYBER_ENCAP_STACK_BURN (algo));
+      return 0;
+
     case GCRY_KEM_MLKEM768:
+      if (pubkey_len != GCRY_KEM_MLKEM768_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM768_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM768_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN)
+	return GPG_ERR_INV_ARG;
+      kyber_encap (algo, ciphertext, shared, pubkey, optional);
+      _gcry_burn_stack (KYBER_ENCAP_STACK_BURN (algo));
+      return 0;
+
     case GCRY_KEM_MLKEM1024:
+      if (pubkey_len != GCRY_KEM_MLKEM1024_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM1024_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM1024_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
       if (optional && optional_len != GCRY_KEM_MLKEM_RANDOM_LEN)
-	return GPG_ERR_INV_VALUE;
+	return GPG_ERR_INV_ARG;
       kyber_encap (algo, ciphertext, shared, pubkey, optional);
       _gcry_burn_stack (KYBER_ENCAP_STACK_BURN (algo));
       return 0;
@@ -200,16 +235,28 @@ _gcry_kem_encap (int algo,
     case GCRY_KEM_RAW_P256R1:
     case GCRY_KEM_RAW_P384R1:
     case GCRY_KEM_RAW_P521R1:
-      if (optional != NULL)
-        return GPG_ERR_INV_VALUE;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       return _gcry_ecc_raw_encap (algo, pubkey, pubkey_len,
                                   ciphertext, ciphertext_len,
                                   shared, shared_len);
 
     case GCRY_KEM_DHKEM25519:
+      if (pubkey_len != GCRY_KEM_DHKEM25519_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_DHKEM25519_ENCAPS_LEN
+          || shared_len != GCRY_KEM_DHKEM25519_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
+      return _gcry_ecc_dhkem_encap (algo, pubkey, ciphertext, shared);
+
     case GCRY_KEM_DHKEM448:
-      if (optional != NULL)
-        return GPG_ERR_INV_VALUE;
+      if (pubkey_len != GCRY_KEM_DHKEM448_PUBKEY_LEN
+          || ciphertext_len != GCRY_KEM_DHKEM448_ENCAPS_LEN
+          || shared_len != GCRY_KEM_DHKEM448_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       return _gcry_ecc_dhkem_encap (algo, pubkey, ciphertext, shared);
 
     default:
@@ -229,29 +276,57 @@ _gcry_kem_decap (int algo,
   switch (algo)
     {
     case GCRY_KEM_SNTRUP761:
-      if (optional != NULL || optional_len != 0)
-        return GPG_ERR_INV_VALUE;
       if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN
           || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN
           || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN)
-        return GPG_ERR_INV_VALUE;
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       sntrup761_dec (shared, ciphertext, seckey);
       _gcry_burn_stack (SNTRUP761_DEC_STACK_BURN);
       return 0;
 
     case GCRY_KEM_CM6688128F:
-      if (optional != NULL)
-	return GPG_ERR_INV_VALUE;
+      if (seckey_len != GCRY_KEM_CM6688128F_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_CM6688128F_ENCAPS_LEN
+          || shared_len != GCRY_KEM_CM6688128F_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+	return GPG_ERR_INV_ARG;
       mceliece6688128f_dec (shared, ciphertext, seckey);
       _gcry_burn_stack (MCELIECE6688128F_DEC_STACK_BURN);
       return 0;
 
 #if USE_KYBER
     case GCRY_KEM_MLKEM512:
+      if (seckey_len != GCRY_KEM_MLKEM512_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM512_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM512_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
+      kyber_decap (algo, shared, ciphertext, seckey);
+      _gcry_burn_stack (KYBER_DECAP_STACK_BURN (algo));
+      return 0;
+
     case GCRY_KEM_MLKEM768:
+      if (seckey_len != GCRY_KEM_MLKEM768_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM768_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM768_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
+      kyber_decap (algo, shared, ciphertext, seckey);
+      _gcry_burn_stack (KYBER_DECAP_STACK_BURN (algo));
+      return 0;
+
     case GCRY_KEM_MLKEM1024:
-      if (optional != NULL)
-        return GPG_ERR_INV_VALUE;
+      if (seckey_len != GCRY_KEM_MLKEM1024_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_MLKEM1024_ENCAPS_LEN
+          || shared_len != GCRY_KEM_MLKEM1024_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       kyber_decap (algo, shared, ciphertext, seckey);
       _gcry_burn_stack (KYBER_DECAP_STACK_BURN (algo));
       return 0;
@@ -265,14 +340,25 @@ _gcry_kem_decap (int algo,
     case GCRY_KEM_RAW_P256R1:
     case GCRY_KEM_RAW_P384R1:
     case GCRY_KEM_RAW_P521R1:
-      if (optional != NULL)
-        return GPG_ERR_INV_VALUE;
+      if (optional != NULL || optional_len != 0)
+        return GPG_ERR_INV_ARG;
       return _gcry_ecc_raw_decap (algo, seckey, seckey_len,
                                   ciphertext, ciphertext_len,
                                   shared, shared_len);
 
     case GCRY_KEM_DHKEM25519:
+      if (seckey_len != GCRY_KEM_DHKEM25519_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_DHKEM25519_ENCAPS_LEN
+          || shared_len != GCRY_KEM_DHKEM25519_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
+      return _gcry_ecc_dhkem_decap (algo, seckey, ciphertext, shared,
+                                    optional);
+
     case GCRY_KEM_DHKEM448:
+      if (seckey_len != GCRY_KEM_DHKEM448_SECKEY_LEN
+          || ciphertext_len != GCRY_KEM_DHKEM448_ENCAPS_LEN
+          || shared_len != GCRY_KEM_DHKEM448_SHARED_LEN)
+        return GPG_ERR_INV_ARG;
       return _gcry_ecc_dhkem_decap (algo, seckey, ciphertext, shared,
                                     optional);
 
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 73e54bf3..562327cb 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1822,6 +1822,19 @@ enum gcry_kem_algos
 #define GCRY_KEM_DHKEM25519_CIPHER_LEN  GCRY_KEM_DHKEM25519_ENCAPS_LEN
 #define GCRY_KEM_DHKEM25519_SHARED_LEN  32
 
+#define GCRY_KEM_ECC_X448_SECKEY_LEN    56
+#define GCRY_KEM_ECC_X448_PUBKEY_LEN    56
+#define GCRY_KEM_ECC_X448_ENCAPS_LEN    56
+#define GCRY_KEM_ECC_X448_CIPHER_LEN    GCRY_KEM_ECC_X448_ENCAPS_LEN
+/* And shared secret is specific to the protocol.  */
+#define GCRY_KEM_RAW_X448_SHARED_LEN    56
+
+#define GCRY_KEM_DHKEM448_SECKEY_LEN    GCRY_KEM_ECC_X448_SECKEY_LEN
+#define GCRY_KEM_DHKEM448_PUBKEY_LEN    GCRY_KEM_ECC_X448_PUBKEY_LEN
+#define GCRY_KEM_DHKEM448_ENCAPS_LEN    GCRY_KEM_ECC_X448_ENCAPS_LEN
+#define GCRY_KEM_DHKEM448_CIPHER_LEN    GCRY_KEM_DHKEM448_ENCAPS_LEN
+#define GCRY_KEM_DHKEM448_SHARED_LEN    56
+
 #define GCRY_KEM_ECC_BP256_SECKEY_LEN   32
 #define GCRY_KEM_ECC_BP256_PUBKEY_LEN   (1+32+32)
 #define GCRY_KEM_ECC_BP256_ENCAPS_LEN   (1+32+32)

--------------2.47.3
Content-Type: text/plain; charset="us-ascii"
MIME-Version: 1.0
Content-Transfer-Encoding: 7bit
Content-Disposition: inline

_______________________________________________
Gcrypt-devel mailing list
[email protected]
https://lists.gnupg.org/mailman/listinfo/gcrypt-devel

--------------2.47.3--