]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
key_share: support X25519Kyber768Draft00
authorDaiki Ueno <ueno@gnu.org>
Sat, 1 Jun 2024 22:19:14 +0000 (07:19 +0900)
committerDaiki Ueno <ueno@gnu.org>
Sat, 20 Jul 2024 23:30:14 +0000 (08:30 +0900)
This implements X25519Kyber768Draft00 hybrid post-quantum key exchange
in TLS 1.3, based on the draft:
https://datatracker.ietf.org/doc/draft-tls-westerbaan-xyber768d00/

Signed-off-by: Daiki Ueno <ueno@gnu.org>
13 files changed:
lib/Makefile.am
lib/algorithms/groups.c
lib/algorithms/publickey.c
lib/crypto-backend.h
lib/ext/key_share.c
lib/gnutls_int.h
lib/includes/gnutls/gnutls.h.in
lib/nettle/Makefile.am
lib/nettle/pk.c
lib/pk.h
lib/state.c
tests/Makefile.am
tests/pqc-hybrid-kx.sh [new file with mode: 0644]

index 067772c322c51f50469fa197e0083d0420e916f6..0e89fdf1842ba6adf2dc4eecbf853c136da0db2a 100644 (file)
@@ -273,6 +273,10 @@ thirdparty_libadd += $(NETTLE_LIBS) $(HOGWEED_LIBS) $(GMP_LIBS)
 libgnutls_la_LIBADD += nettle/libcrypto.la
 endif
 
+if ENABLE_LIBOQS
+libgnutls_la_LIBADD += liboqs/libcrypto.la
+endif
+
 if HAVE_LD_OUTPUT_DEF
 libgnutls_la_LDFLAGS += -Wl,--output-def,libgnutls-$(DLL_VERSION).def
 libgnutls-$(DLL_VERSION).def: libgnutls.la
index 9093de6eb2fcddf90c8cd7c51bce09593f05762b..a329c746e9f102e4bbb8e680f56b87ff56209505 100644 (file)
@@ -169,6 +169,14 @@ static const gnutls_group_entry_st supported_groups[] = {
          .q_bits = &gnutls_ffdhe_8192_key_bits,
          .pk = GNUTLS_PK_DH,
          .tls_id = 0x104 },
+#endif
+#ifdef HAVE_LIBOQS
+       { .name = "X25519-KYBER768",
+         .id = GNUTLS_GROUP_EXP_X25519_KYBER768,
+         .curve = GNUTLS_ECC_CURVE_X25519,
+         .pk = GNUTLS_PK_ECDH_X25519,
+         .pk2 = GNUTLS_PK_EXP_KYBER768,
+         .tls_id = 0x6399 },
 #endif
        { 0, 0, 0 }
 };
index 0ef0834933ca0f556ccf529d4ea09f297cae1da3..10938bce0e8baabdf4f06b792d946da0c0c3bbd9 100644 (file)
@@ -202,6 +202,12 @@ static const gnutls_pk_entry pk_algorithms[] = {
          .oid = ECDH_X448_OID,
          .id = GNUTLS_PK_ECDH_X448,
          .curve = GNUTLS_ECC_CURVE_X448 },
+#ifdef HAVE_LIBOQS
+       { .name = "KYBER768",
+         .oid = NULL,
+         .id = GNUTLS_PK_EXP_KYBER768,
+         .curve = GNUTLS_ECC_CURVE_INVALID },
+#endif
        { .name = "UNKNOWN",
          .oid = NULL,
          .id = GNUTLS_PK_UNKNOWN,
index fd8ee0c72879f0572f18f1f541b3284dafd9b2bd..5c0630adaa72393984eac32534974d9bc5b74345 100644 (file)
@@ -411,6 +411,13 @@ typedef struct gnutls_crypto_pk {
                      const gnutls_pk_params_st *pub,
                      const gnutls_datum_t *nonce, unsigned int flags);
 
+       int (*encaps)(gnutls_pk_algorithm_t, gnutls_datum_t *ciphertext,
+                     gnutls_datum_t *shared_secret, const gnutls_datum_t *pub);
+
+       int (*decaps)(gnutls_pk_algorithm_t, gnutls_datum_t *shared_secret,
+                     const gnutls_datum_t *ciphertext,
+                     const gnutls_datum_t *priv);
+
        int (*curve_exists)(gnutls_ecc_curve_t); /* true/false */
        int (*pk_exists)(gnutls_pk_algorithm_t); /* true/false */
        int (*sign_exists)(gnutls_sign_algorithm_t); /* true/false */
index 575ffaf8f2b9ecb4cacd9b0e0f9ac7f20b570e6b..6926cdd00ead397704a2df390bc02de1c8c4b3e5 100644 (file)
@@ -120,6 +120,8 @@ static int client_gen_key_share(gnutls_session_t session,
 
        } else if (group->pk == GNUTLS_PK_ECDH_X25519 ||
                   group->pk == GNUTLS_PK_ECDH_X448) {
+               unsigned int length_pos;
+
                gnutls_pk_params_release(&session->key.kshare.ecdhx_params);
                gnutls_pk_params_init(&session->key.kshare.ecdhx_params);
 
@@ -129,6 +131,8 @@ static int client_gen_key_share(gnutls_session_t session,
                if (ret < 0)
                        return gnutls_assert_val(ret);
 
+               length_pos = extdata->length;
+
                ret = _gnutls_buffer_append_data_prefix(
                        extdata, 16,
                        session->key.kshare.ecdhx_params.raw_pub.data,
@@ -141,6 +145,33 @@ static int client_gen_key_share(gnutls_session_t session,
                session->key.kshare.ecdhx_params.algo = group->pk;
                session->key.kshare.ecdhx_params.curve = group->curve;
 
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       gnutls_pk_params_release(
+                               &session->key.kshare.kem_params);
+                       gnutls_pk_params_init(&session->key.kshare.kem_params);
+
+                       ret = _gnutls_pk_generate_keys(
+                               group->pk2, 0, &session->key.kshare.kem_params,
+                               1);
+                       if (ret < 0) {
+                               gnutls_assert();
+                               goto cleanup;
+                       }
+
+                       ret = _gnutls_buffer_append_data(
+                               extdata,
+                               session->key.kshare.kem_params.raw_pub.data,
+                               session->key.kshare.kem_params.raw_pub.size);
+                       if (ret < 0) {
+                               gnutls_assert();
+                               goto cleanup;
+                       }
+
+                       /* copy actual length */
+                       _gnutls_write_uint16(extdata->length - length_pos - 2,
+                                            &extdata->data[length_pos]);
+               }
+
                ret = 0;
 
        } else if (group->pk == GNUTLS_PK_DH) {
@@ -243,6 +274,10 @@ static int server_gen_key_share(gnutls_session_t session,
 
        } else if (group->pk == GNUTLS_PK_ECDH_X25519 ||
                   group->pk == GNUTLS_PK_ECDH_X448) {
+               unsigned int length_pos;
+
+               length_pos = extdata->length;
+
                ret = _gnutls_buffer_append_data_prefix(
                        extdata, 16,
                        session->key.kshare.ecdhx_params.raw_pub.data,
@@ -250,8 +285,22 @@ static int server_gen_key_share(gnutls_session_t session,
                if (ret < 0)
                        return gnutls_assert_val(ret);
 
-               ret = 0;
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       ret = _gnutls_buffer_append_data(
+                               extdata,
+                               session->key.kshare.kem_params.raw_pub.data,
+                               session->key.kshare.kem_params.raw_pub.size);
+                       if (ret < 0) {
+                               gnutls_assert();
+                               goto cleanup;
+                       }
 
+                       /* copy actual length */
+                       _gnutls_write_uint16(extdata->length - length_pos - 2,
+                                            &extdata->data[length_pos]);
+               }
+
+               ret = 0;
        } else if (group->pk == GNUTLS_PK_DH) {
                ret = _gnutls_buffer_append_prefix(extdata, 16,
                                                   group->prime->size);
@@ -333,9 +382,15 @@ static int server_use_key_share(gnutls_session_t session,
 
                curve = _gnutls_ecc_curve_get_params(group->curve);
 
-               if (curve->size != data_size)
-                       return gnutls_assert_val(
-                               GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       if (curve->size > data_size)
+                               return gnutls_assert_val(
+                                       GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               } else {
+                       if (curve->size != data_size)
+                               return gnutls_assert_val(
+                                       GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               }
 
                /* generate our key */
                ret = _gnutls_pk_generate_keys(
@@ -351,7 +406,7 @@ static int server_use_key_share(gnutls_session_t session,
                pub.curve = curve->id;
 
                pub.raw_pub.data = (void *)data;
-               pub.raw_pub.size = data_size;
+               pub.raw_pub.size = curve->size;
 
                /* We don't mask the MSB in the final byte as required
                 * by RFC7748. This will be done internally by nettle 3.3 or later.
@@ -363,6 +418,50 @@ static int server_use_key_share(gnutls_session_t session,
                        return gnutls_assert_val(ret);
                }
 
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       gnutls_datum_t key;
+                       gnutls_datum_t peer_pub;
+
+                       gnutls_pk_params_release(
+                               &session->key.kshare.kem_params);
+                       gnutls_pk_params_init(&session->key.kshare.kem_params);
+
+                       /* generate our key */
+                       ret = _gnutls_pk_generate_keys(
+                               group->pk2, 0, &session->key.kshare.kem_params,
+                               1);
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+
+                       /* server's public key is unused, but the raw_pub field
+                        * is used to store ciphertext */
+                       gnutls_free(
+                               session->key.kshare.kem_params.raw_pub.data);
+
+                       peer_pub.data = (uint8_t *)data + curve->size;
+                       peer_pub.size = data_size - curve->size;
+
+                       ret = _gnutls_pk_encaps(
+                               group->pk2,
+                               &session->key.kshare.kem_params.raw_pub, &key,
+                               &peer_pub);
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+
+                       session->key.key.data = gnutls_realloc_fast(
+                               session->key.key.data,
+                               session->key.key.size + key.size);
+                       if (!session->key.key.data) {
+                               _gnutls_free_datum(&key);
+                               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       }
+
+                       memcpy(session->key.key.data + session->key.key.size,
+                              key.data, key.size);
+                       session->key.key.size += key.size;
+                       _gnutls_free_datum(&key);
+               }
+
                ret = 0;
 
        } else if (group->pk == GNUTLS_PK_DH) {
@@ -496,9 +595,15 @@ static int client_use_key_share(gnutls_session_t session,
                        return gnutls_assert_val(
                                GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
 
-               if (curve->size != data_size)
-                       return gnutls_assert_val(
-                               GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       if (curve->size > data_size)
+                               return gnutls_assert_val(
+                                       GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               } else {
+                       if (curve->size != data_size)
+                               return gnutls_assert_val(
+                                       GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+               }
 
                /* read the public key and generate shared */
                gnutls_pk_params_init(&pub);
@@ -519,6 +624,33 @@ static int client_use_key_share(gnutls_session_t session,
                        return gnutls_assert_val(ret);
                }
 
+               if (group->pk2 != GNUTLS_PK_UNKNOWN) {
+                       gnutls_datum_t key;
+                       gnutls_datum_t ciphertext;
+
+                       ciphertext.data = (uint8_t *)data + curve->size;
+                       ciphertext.size = data_size - curve->size;
+
+                       ret = _gnutls_pk_decaps(
+                               group->pk2, &key, &ciphertext,
+                               &session->key.kshare.kem_params.raw_priv);
+                       if (ret < 0)
+                               return gnutls_assert_val(ret);
+
+                       session->key.key.data = gnutls_realloc_fast(
+                               session->key.key.data,
+                               session->key.key.size + key.size);
+                       if (!session->key.key.data) {
+                               _gnutls_free_datum(&key);
+                               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       }
+
+                       memcpy(session->key.key.data + session->key.key.size,
+                              key.data, key.size);
+                       session->key.key.size += key.size;
+                       _gnutls_free_datum(&key);
+               }
+
                ret = 0;
 
        } else if (group->pk == GNUTLS_PK_DH) {
index 258a08c8427098088222c267beabd326dd4edde5..5727739bdc581fbf7a190b376f09408f1e822715 100644 (file)
@@ -594,6 +594,7 @@ struct gnutls_key_st {
                gnutls_pk_params_st ecdh_params;
                gnutls_pk_params_st ecdhx_params;
                gnutls_pk_params_st dh_params;
+               gnutls_pk_params_st kem_params;
        } kshare;
 
        /* The union contents depend on the negotiated protocol.
@@ -764,6 +765,7 @@ typedef struct gnutls_group_entry_st {
        const unsigned *q_bits;
        gnutls_ecc_curve_t curve;
        gnutls_pk_algorithm_t pk;
+       gnutls_pk_algorithm_t pk2;
        unsigned tls_id; /* The RFC4492 namedCurve ID or TLS 1.3 group ID */
 } gnutls_group_entry_st;
 
index 6b87610c443b122c373fd2a8c08397f922fbe8f9..790406e4df82410c0a81266e362c6f80e4e544f5 100644 (file)
@@ -908,7 +908,12 @@ typedef enum {
        GNUTLS_PK_ECDH_X448 = 11,
        GNUTLS_PK_EDDSA_ED448 = 12,
        GNUTLS_PK_RSA_OAEP = 13,
-       GNUTLS_PK_MAX = GNUTLS_PK_RSA_OAEP
+       GNUTLS_PK_MAX = GNUTLS_PK_RSA_OAEP,
+
+       /* Experimental algorithms */
+       GNUTLS_PK_EXP_MIN = 256,
+       GNUTLS_PK_EXP_KYBER768 = GNUTLS_PK_EXP_MIN,
+       GNUTLS_PK_EXP_MAX = GNUTLS_PK_EXP_KYBER768
 } gnutls_pk_algorithm_t;
 
 const char *gnutls_pk_algorithm_get_name(gnutls_pk_algorithm_t algorithm);
@@ -1136,6 +1141,11 @@ typedef enum {
        GNUTLS_GROUP_FFDHE8192,
        GNUTLS_GROUP_FFDHE6144,
        GNUTLS_GROUP_MAX = GNUTLS_GROUP_FFDHE6144,
+
+       /* Experimental algorithms */
+       GNUTLS_GROUP_EXP_MIN = 512,
+       GNUTLS_GROUP_EXP_X25519_KYBER768 = GNUTLS_GROUP_EXP_MIN,
+       GNUTLS_GROUP_EXP_MAX = GNUTLS_GROUP_EXP_X25519_KYBER768
 } gnutls_group_t;
 
 /* macros to allow specifying a specific curve in gnutls_privkey_generate()
index b855c8c193fc50f92e6f7150a0c7a889bad12919..0f21823cb49960c68e924879ac636c8c88d5a0ca 100644 (file)
@@ -36,6 +36,10 @@ if ENABLE_MINITASN1
 AM_CPPFLAGS += -I$(srcdir)/../minitasn1
 endif
 
+if ENABLE_DLOPEN
+AM_CPPFLAGS += $(LIBOQS_CFLAGS) -DGNUTLS_OQS_ENABLE_DLOPEN=1
+endif
+
 noinst_LTLIBRARIES = libcrypto.la
 
 libcrypto_la_SOURCES = pk.c mpi.c mac.c cipher.c init.c \
index b317b790d7c97e8c7cc70be3133dc870b5cb4fa3..4155a540ed798672c5eb915e25c5928b7f82e80f 100644 (file)
@@ -70,6 +70,9 @@
 #include "gnettle.h"
 #include "fips.h"
 #include "dh.h"
+#ifdef HAVE_LIBOQS
+#include "dlwrap/oqs.h"
+#endif
 
 static inline const struct ecc_curve *get_supported_nist_curve(int curve);
 static inline const struct ecc_curve *get_supported_gost_curve(int curve);
@@ -687,6 +690,111 @@ cleanup:
        return ret;
 }
 
+static int _wrap_nettle_pk_encaps(gnutls_pk_algorithm_t algo,
+                                 gnutls_datum_t *ciphertext,
+                                 gnutls_datum_t *shared_secret,
+                                 const gnutls_datum_t *pub)
+{
+       int ret;
+
+       switch (algo) {
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768: {
+               OQS_KEM *kem = NULL;
+               OQS_STATUS rc;
+
+               kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+               if (kem == NULL)
+                       return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+               ciphertext->data = gnutls_malloc(kem->length_ciphertext);
+               if (ciphertext->data == NULL) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+               ciphertext->size = kem->length_ciphertext;
+
+               shared_secret->data = gnutls_malloc(kem->length_shared_secret);
+               if (shared_secret->data == NULL) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+               shared_secret->size = kem->length_shared_secret;
+
+               rc = GNUTLS_OQS_FUNC(OQS_KEM_encaps)(
+                       kem, ciphertext->data, shared_secret->data, pub->data);
+               if (rc != OQS_SUCCESS) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+                       goto cleanup;
+               }
+
+               GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+               ret = 0;
+       } break;
+#endif
+       default:
+               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+               goto cleanup;
+       }
+
+cleanup:
+       if (ret < 0) {
+               gnutls_free(ciphertext->data);
+               gnutls_free(shared_secret->data);
+       }
+       return ret;
+}
+
+static int _wrap_nettle_pk_decaps(gnutls_pk_algorithm_t algo,
+                                 gnutls_datum_t *shared_secret,
+                                 const gnutls_datum_t *ciphertext,
+                                 const gnutls_datum_t *priv)
+{
+       int ret;
+
+       switch (algo) {
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768: {
+               OQS_KEM *kem = NULL;
+               OQS_STATUS rc;
+
+               kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+               if (kem == NULL)
+                       return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+
+               shared_secret->data = gnutls_malloc(kem->length_shared_secret);
+               if (shared_secret->data == NULL) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+               shared_secret->size = kem->length_shared_secret;
+
+               rc = GNUTLS_OQS_FUNC(OQS_KEM_decaps)(
+                       kem, shared_secret->data, ciphertext->data, priv->data);
+               if (rc != OQS_SUCCESS) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+                       goto cleanup;
+               }
+
+               GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+               ret = 0;
+       } break;
+#endif
+       default:
+               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_ALGORITHM);
+               goto cleanup;
+       }
+cleanup:
+       if (ret < 0)
+               gnutls_free(shared_secret->data);
+       return ret;
+}
+
 /* This wraps nettle_rsa_encrypt so it returns ciphertext as a byte
  * array instead of a mpz_t value.  Returns 1 on success; 0 otherwise.
  */
@@ -2234,6 +2342,9 @@ static int _wrap_nettle_pk_exists(gnutls_pk_algorithm_t pk)
        case GNUTLS_PK_RSA_PSS:
        case GNUTLS_PK_RSA_OAEP:
        case GNUTLS_PK_EDDSA_ED25519:
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768:
+#endif
 #if ENABLE_GOST
        case GNUTLS_PK_GOST_01:
        case GNUTLS_PK_GOST_12_256:
@@ -2875,6 +2986,9 @@ static int pct_test(gnutls_pk_algorithm_t algo,
        }
        case GNUTLS_PK_ECDH_X25519:
        case GNUTLS_PK_ECDH_X448:
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768:
+#endif
                ret = 0;
                goto cleanup;
        default:
@@ -3605,6 +3719,49 @@ wrap_nettle_pk_generate_keys(gnutls_pk_algorithm_t algo,
                        goto cleanup;
                break;
        }
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768: {
+               OQS_KEM *kem = NULL;
+               OQS_STATUS rc;
+
+               not_approved = true;
+
+               kem = GNUTLS_OQS_FUNC(OQS_KEM_new)(OQS_KEM_alg_kyber_768);
+               if (kem == NULL) {
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+
+               params->raw_priv.size = kem->length_secret_key;
+               params->raw_priv.data = gnutls_malloc(params->raw_priv.size);
+               if (params->raw_priv.data == NULL) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+
+               params->raw_pub.size = kem->length_public_key;
+               params->raw_pub.data = gnutls_malloc(params->raw_pub.size);
+               if (params->raw_pub.data == NULL) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+
+               rc = GNUTLS_OQS_FUNC(OQS_KEM_keypair)(kem, params->raw_pub.data,
+                                                     params->raw_priv.data);
+               if (rc != OQS_SUCCESS) {
+                       GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+                       ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+                       goto cleanup;
+               }
+
+               GNUTLS_OQS_FUNC(OQS_KEM_free)(kem);
+
+               ret = 0;
+               break;
+       }
+#endif
        default:
                gnutls_assert();
                return GNUTLS_E_INVALID_REQUEST;
@@ -3858,6 +4015,11 @@ static int wrap_nettle_pk_verify_priv_params(gnutls_pk_algorithm_t algo,
                ret = 0;
                break;
        }
+#ifdef HAVE_LIBOQS
+       case GNUTLS_PK_EXP_KYBER768:
+               ret = 0;
+               break;
+#endif
 #if ENABLE_GOST
        case GNUTLS_PK_GOST_01:
        case GNUTLS_PK_GOST_12_256:
@@ -4307,6 +4469,8 @@ gnutls_crypto_pk_st _gnutls_pk_ops = {
        .generate_keys = wrap_nettle_pk_generate_keys,
        .pk_fixup_private_params = wrap_nettle_pk_fixup,
        .derive = _wrap_nettle_pk_derive,
+       .encaps = _wrap_nettle_pk_encaps,
+       .decaps = _wrap_nettle_pk_decaps,
        .curve_exists = _wrap_nettle_pk_curve_exists,
        .pk_exists = _wrap_nettle_pk_exists,
        .sign_exists = _wrap_nettle_pk_sign_exists
index 20fe314f94b670d81e41a80aaaaee5cbf1d2153c..eca4e02d734a6cec6c11e0a431307c9c033aa5ab 100644 (file)
--- a/lib/pk.h
+++ b/lib/pk.h
@@ -46,6 +46,10 @@ extern gnutls_crypto_pk_st _gnutls_pk_ops;
        _gnutls_pk_ops.derive(algo, out, pub, priv, nonce, 0)
 #define _gnutls_pk_derive_tls13(algo, out, pub, priv) \
        _gnutls_pk_ops.derive(algo, out, pub, priv, NULL, PK_DERIVE_TLS13)
+#define _gnutls_pk_encaps(algo, ciphertext, shared_secret, pub) \
+       _gnutls_pk_ops.encaps(algo, ciphertext, shared_secret, pub)
+#define _gnutls_pk_decaps(algo, shared_secret, ciphertext, priv) \
+       _gnutls_pk_ops.decaps(algo, shared_secret, ciphertext, priv)
 #define _gnutls_pk_generate_keys(algo, bits, params, temporal) \
        _gnutls_pk_ops.generate_keys(algo, bits, params, temporal)
 #define _gnutls_pk_generate_params(algo, bits, priv) \
index ec514c0cd288dfa67590e2b566e77f153e58a1f4..f2c74d97d0b1018f7803272c3846829825163207 100644 (file)
@@ -459,6 +459,7 @@ static void deinit_keys(gnutls_session_t session)
        gnutls_pk_params_release(&session->key.kshare.ecdhx_params);
        gnutls_pk_params_release(&session->key.kshare.ecdh_params);
        gnutls_pk_params_release(&session->key.kshare.dh_params);
+       gnutls_pk_params_release(&session->key.kshare.kem_params);
 
        if (!vers->tls13_sem && session->key.binders[0].prf == NULL) {
                gnutls_pk_params_release(&session->key.proto.tls12.ecdh.params);
index c674835c1ff5c71835b90ad0c21645b9bbd3416b..ca76736d2e21bf7d21e70b2288912e61fe071e48 100644 (file)
@@ -600,6 +600,10 @@ ctests += win32-certopenstore
 
 endif
 
+if ENABLE_LIBOQS
+dist_check_SCRIPTS += pqc-hybrid-kx.sh
+endif
+
 cpptests =
 if ENABLE_CXX
 if HAVE_CMOCKA
diff --git a/tests/pqc-hybrid-kx.sh b/tests/pqc-hybrid-kx.sh
new file mode 100644 (file)
index 0000000..b9302b4
--- /dev/null
@@ -0,0 +1,54 @@
+#!/bin/sh
+
+# Copyright (C) 2022 Red Hat, Inc.
+#
+# Author: Daiki Ueno
+#
+# This file is part of GnuTLS.
+#
+# GnuTLS is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by the
+# Free Software Foundation; either version 3 of the License, or (at
+# your option) any later version.
+#
+# GnuTLS is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GnuTLS. If not, see <https://www.gnu.org/licenses/>.
+
+: ${srcdir=.}
+: ${SERV=../src/gnutls-serv${EXEEXT}}
+: ${CLI=../src/gnutls-cli${EXEEXT}}
+
+if ! test -x "${SERV}"; then
+       exit 77
+fi
+
+if ! test -x "${CLI}"; then
+       exit 77
+fi
+
+. "${srcdir}/scripts/common.sh"
+testdir=`create_testdir pqc-hybrid-kx`
+
+KEY="$srcdir/../doc/credentials/x509/key-ed25519.pem"
+CERT="$srcdir/../doc/credentials/x509/cert-ed25519.pem"
+CACERT="$srcdir/../doc/credentials/x509/ca.pem"
+
+eval "${GETPORT}"
+launch_server --echo --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509keyfile="$KEY" --x509certfile="$CERT"
+PID=$!
+wait_server ${PID}
+
+${VALGRIND} "${CLI}" -p "${PORT}" 127.0.0.1 --priority NORMAL:-GROUP-ALL:+GROUP-X25519-KYBER768 --x509cafile="$CACERT" --logfile="$testdir/cli.log" </dev/null
+
+kill ${PID}
+wait
+
+grep -- '- Description: (TLS1.3-X.509)-(ECDHE-X25519-KYBER768)-(EdDSA-Ed25519)-(AES-256-GCM)' "$testdir/cli.log" || { echo "unexpected handshake description"; exit 1; }
+
+rm -rf "$testdir"
+exit 0