]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
HPKE API rework.
authord-Dudas <david.dudas03@e-uvt.ro>
Sun, 15 Mar 2026 06:44:19 +0000 (08:44 +0200)
committerd-Dudas <david.dudas03@e-uvt.ro>
Sat, 18 Apr 2026 06:26:57 +0000 (09:26 +0300)
Aligned the HPKE with RFC9180.

Signed-off-by: David Dudas <david.dudas03@e-uvt.ro>
22 files changed:
.gitignore
devel/libgnutls.abignore
devel/symbols.last
doc/Makefile.am
doc/manpages/Makefile.am
lib/Makefile.am
lib/hpke-api.c [deleted file]
lib/hpke/helpers/hpke-builders.c [new file with mode: 0644]
lib/hpke/helpers/hpke-builders.h [new file with mode: 0644]
lib/hpke/helpers/hpke-hkdf.c [new file with mode: 0644]
lib/hpke/helpers/hpke-hkdf.h [new file with mode: 0644]
lib/hpke/helpers/hpke-key-management.c [new file with mode: 0644]
lib/hpke/helpers/hpke-key-management.h [new file with mode: 0644]
lib/hpke/helpers/hpke-params.c [new file with mode: 0644]
lib/hpke/helpers/hpke-params.h [new file with mode: 0644]
lib/hpke/hpke.c [new file with mode: 0644]
lib/includes/gnutls/abstract.h
lib/libgnutls.map
lib/x509/privkey.c
tests/Makefile.am
tests/hpke-tests.c [new file with mode: 0644]
tests/hpke.c [deleted file]

index c10d0e7915137f5fc6f1c20e27800f67f18c520a..952f1147219df5e25eddaa657223ecd02a736ae1 100644 (file)
@@ -438,7 +438,7 @@ tests/handshake-write
 tests/hex
 tests/hostname-check
 tests/hostname-check-utf8
-tests/hpke
+tests/hpke-tests
 tests/id-on-xmppAddr
 tests/infoaccess
 tests/init_roundtrip
index 61392594a8b70b7f61f2388948678562e207879d..e3dc01455fb51e999c0e9888e8b7f8bd8c22a5b7 100644 (file)
@@ -75,3 +75,42 @@ name = gnutls_hpke_encap
 
 [suppress_function]
 name = gnutls_hpke_decap
+
+[suppress_function]
+name = gnutls_hpke_context_init
+
+[suppress_function]
+name = gnutls_hpke_context_deinit
+
+[suppress_function]
+name = gnutls_hpke_context_set_psk
+
+[suppress_function]
+name = gnutls_hpke_context_set_sender_privkey
+
+[suppress_function]
+name = gnutls_hpke_context_set_sender_pubkey
+
+[suppress_function]
+name = gnutls_hpke_context_get_enc_size
+
+[suppress_function]
+name = gnutls_hpke_seal
+
+[suppress_function]
+name = gnutls_hpke_open
+
+[suppress_function]
+name = gnutls_hpke_context_set_ikme
+
+[suppress_function]
+name = gnutls_hpke_generate_keypair
+
+[suppress_function]
+name = gnutls_hpke_get_seq
+
+[suppress_function]
+name = gnutls_hpke_set_seq
+
+[suppress_function]
+name = gnutls_hpke_export
index c9c00c76791e5cbd663d81d2cd19823d3d3e806b..ef6c9230f59b88e4b24955c1f40503d4d5f4940e 100644 (file)
@@ -18,7 +18,7 @@ GNUTLS_3_7_4@GNUTLS_3_7_4
 GNUTLS_3_7_5@GNUTLS_3_7_5
 GNUTLS_3_7_7@GNUTLS_3_7_7
 GNUTLS_3_8_11@GNUTLS_3_8_11
-GNUTLS_3_8_13@GNUTLS_3_8_13
+GNUTLS_3_8_12@GNUTLS_3_8_12
 GNUTLS_3_8_1@GNUTLS_3_8_1
 GNUTLS_3_8_2@GNUTLS_3_8_2
 GNUTLS_3_8_4@GNUTLS_3_8_4
@@ -337,6 +337,21 @@ gnutls_hmac_get_len@GNUTLS_3_4
 gnutls_hmac_init@GNUTLS_3_4
 gnutls_hmac_output@GNUTLS_3_4
 gnutls_hmac_set_nonce@GNUTLS_3_4
+gnutls_hpke_context_deinit@GNUTLS_3_8_12
+gnutls_hpke_context_get_enc_size@GNUTLS_3_8_12
+gnutls_hpke_context_init@GNUTLS_3_8_12
+gnutls_hpke_context_set_ikme@GNUTLS_3_8_12
+gnutls_hpke_context_set_psk@GNUTLS_3_8_12
+gnutls_hpke_context_set_sender_privkey@GNUTLS_3_8_12
+gnutls_hpke_context_set_sender_pubkey@GNUTLS_3_8_12
+gnutls_hpke_decap@GNUTLS_3_8_12
+gnutls_hpke_encap@GNUTLS_3_8_12
+gnutls_hpke_export@GNUTLS_3_8_12
+gnutls_hpke_generate_keypair@GNUTLS_3_8_12
+gnutls_hpke_get_seq@GNUTLS_3_8_12
+gnutls_hpke_open@GNUTLS_3_8_12
+gnutls_hpke_seal@GNUTLS_3_8_12
+gnutls_hpke_set_seq@GNUTLS_3_8_12
 gnutls_idna_map@GNUTLS_3_4
 gnutls_idna_reverse_map@GNUTLS_3_4
 gnutls_init@GNUTLS_3_4
@@ -1319,5 +1334,3 @@ gnutls_x509_trust_list_set_ptr@GNUTLS_3_7_0
 gnutls_x509_trust_list_verify_crt2@GNUTLS_3_4
 gnutls_x509_trust_list_verify_crt@GNUTLS_3_4
 gnutls_x509_trust_list_verify_named_crt@GNUTLS_3_4
-gnutls_hpke_encap@GNUTLS_3_8_12
-gnutls_hpke_decap@GNUTLS_3_8_12
index e5df565faac391c0d69e28617ce2825da9feb34a..7592675d038c36d0a924705a78facaee39671f71 100644 (file)
@@ -1212,6 +1212,36 @@ FUNCS += functions/gnutls_hmac_output
 FUNCS += functions/gnutls_hmac_output.short
 FUNCS += functions/gnutls_hmac_set_nonce
 FUNCS += functions/gnutls_hmac_set_nonce.short
+FUNCS += functions/gnutls_hpke_context_deinit
+FUNCS += functions/gnutls_hpke_context_deinit.short
+FUNCS += functions/gnutls_hpke_context_get_enc_size
+FUNCS += functions/gnutls_hpke_context_get_enc_size.short
+FUNCS += functions/gnutls_hpke_context_init
+FUNCS += functions/gnutls_hpke_context_init.short
+FUNCS += functions/gnutls_hpke_context_set_ikme
+FUNCS += functions/gnutls_hpke_context_set_ikme.short
+FUNCS += functions/gnutls_hpke_context_set_psk
+FUNCS += functions/gnutls_hpke_context_set_psk.short
+FUNCS += functions/gnutls_hpke_context_set_sender_privkey
+FUNCS += functions/gnutls_hpke_context_set_sender_privkey.short
+FUNCS += functions/gnutls_hpke_context_set_sender_pubkey
+FUNCS += functions/gnutls_hpke_context_set_sender_pubkey.short
+FUNCS += functions/gnutls_hpke_decap
+FUNCS += functions/gnutls_hpke_decap.short
+FUNCS += functions/gnutls_hpke_encap
+FUNCS += functions/gnutls_hpke_encap.short
+FUNCS += functions/gnutls_hpke_export
+FUNCS += functions/gnutls_hpke_export.short
+FUNCS += functions/gnutls_hpke_generate_keypair
+FUNCS += functions/gnutls_hpke_generate_keypair.short
+FUNCS += functions/gnutls_hpke_get_seq
+FUNCS += functions/gnutls_hpke_get_seq.short
+FUNCS += functions/gnutls_hpke_open
+FUNCS += functions/gnutls_hpke_open.short
+FUNCS += functions/gnutls_hpke_seal
+FUNCS += functions/gnutls_hpke_seal.short
+FUNCS += functions/gnutls_hpke_set_seq
+FUNCS += functions/gnutls_hpke_set_seq.short
 FUNCS += functions/gnutls_idna_map
 FUNCS += functions/gnutls_idna_map.short
 FUNCS += functions/gnutls_idna_reverse_map
@@ -3022,7 +3052,3 @@ FUNCS += functions/gnutls_x509_trust_list_verify_crt2
 FUNCS += functions/gnutls_x509_trust_list_verify_crt2.short
 FUNCS += functions/gnutls_x509_trust_list_verify_named_crt
 FUNCS += functions/gnutls_x509_trust_list_verify_named_crt.short
-FUNCS += functions/gnutls_hpke_encap
-FUNCS += functions/gnutls_hpke_encap.short
-FUNCS += functions/gnutls_hpke_decap
-FUNCS += functions/gnutls_hpke_decap.short
index 128e4d3b9790b0ac7886565505047c10ecf9717a..0e53c3625e0bccf7a4e72b6bea115ea95b3ffafd 100644 (file)
@@ -446,6 +446,21 @@ APIMANS += gnutls_hmac_get_len.3
 APIMANS += gnutls_hmac_init.3
 APIMANS += gnutls_hmac_output.3
 APIMANS += gnutls_hmac_set_nonce.3
+APIMANS += gnutls_hpke_context_deinit.3
+APIMANS += gnutls_hpke_context_get_enc_size.3
+APIMANS += gnutls_hpke_context_init.3
+APIMANS += gnutls_hpke_context_set_ikme.3
+APIMANS += gnutls_hpke_context_set_psk.3
+APIMANS += gnutls_hpke_context_set_sender_privkey.3
+APIMANS += gnutls_hpke_context_set_sender_pubkey.3
+APIMANS += gnutls_hpke_decap.3
+APIMANS += gnutls_hpke_encap.3
+APIMANS += gnutls_hpke_export.3
+APIMANS += gnutls_hpke_generate_keypair.3
+APIMANS += gnutls_hpke_get_seq.3
+APIMANS += gnutls_hpke_open.3
+APIMANS += gnutls_hpke_seal.3
+APIMANS += gnutls_hpke_set_seq.3
 APIMANS += gnutls_idna_map.3
 APIMANS += gnutls_idna_reverse_map.3
 APIMANS += gnutls_init.3
@@ -1351,8 +1366,6 @@ APIMANS += gnutls_x509_trust_list_set_ptr.3
 APIMANS += gnutls_x509_trust_list_verify_crt.3
 APIMANS += gnutls_x509_trust_list_verify_crt2.3
 APIMANS += gnutls_x509_trust_list_verify_named_crt.3
-APIMANS += gnutls_hpke_encap.3
-APIMANS += gnutls_hpke_decap.3
 
 if ENABLE_DOC
 man_MANS += $(APIMANS)
index 1063a7e793c044cf69120a8b66cc2fc4b8769edb..26da581af394a74095107c4f0005f34d418a0eec 100644 (file)
@@ -90,7 +90,11 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
        crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \
        hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \
        iov.c iov.h system/ktls.c system/ktls.h pathbuf.c pathbuf.h \
-       audit.h audit.c crau/crau.h crau/macros.h hpke-api.c
+       audit.h audit.c crau/crau.h crau/macros.h hpke/hpke.c \ 
+       hpke/helpers/hpke-params.c hpke/helpers/hpke-params.h  \
+       hpke/helpers/hpke-builders.c hpke/helpers/hpke-builders.h \
+       hpke/helpers/hpke-key-management.h hpke/helpers/hpke-key-management.c \
+       hpke/helpers/hpke-hkdf.h hpke/helpers/hpke-hkdf.c
 
 if HAVE_ZLIB
 COBJECTS += dlwrap/zlib.c dlwrap/zlibfuncs.h dlwrap/zlib.h
diff --git a/lib/hpke-api.c b/lib/hpke-api.c
deleted file mode 100644 (file)
index bd9ac52..0000000
+++ /dev/null
@@ -1,1517 +0,0 @@
-/*
- * Copyright © 2025 David Dudas
- *
- * Author: David Dudas <david.dudas03@e-uvt.ro>
- *
- * This file is part of GnuTLS.
- *
- * The GnuTLS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
-
- *
- * You should have received a copy of the GNU Lesser General Public License
- * along with this program.  If not, see <https://www.gnu.org/licenses/>
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif /* HAVE_CONFIG_H */
-
-#include <lib/errors.h>
-
-static const gnutls_datum_t empty_datum = { (unsigned char *)"", 0 };
-
-typedef enum hpke_mode_t {
-       GNUTLS_HPKE_MODE_BASE = 0x00,
-       GNUTLS_HPKE_MODE_PSK = 0x01,
-       GNUTLS_HPKE_MODE_AUTH = 0x02,
-       GNUTLS_HPKE_MODE_AUTH_PSK = 0x03
-} gnutls_hpke_mode_t;
-
-static int is_dhkem(const gnutls_hpke_kem_t kem)
-{
-       switch (kem) {
-       case GNUTLS_HPKE_KEM_DHKEM_P256:
-       case GNUTLS_HPKE_KEM_DHKEM_P384:
-       case GNUTLS_HPKE_KEM_DHKEM_P521:
-       case GNUTLS_HPKE_KEM_DHKEM_X25519:
-       case GNUTLS_HPKE_KEM_DHKEM_X448:
-               return 1;
-       default:
-               return 0;
-       }
-}
-
-static int _gnutls_hpke_is_auth_mode(const gnutls_hpke_mode_t mode)
-{
-       return mode == GNUTLS_HPKE_MODE_AUTH ||
-              mode == GNUTLS_HPKE_MODE_AUTH_PSK;
-}
-
-static int _gnutls_is_key_curve_type_compatible_with_param_dhkem(
-       const gnutls_hpke_kem_t kem, const gnutls_ecc_curve_t curve)
-{
-       switch (kem) {
-       case GNUTLS_HPKE_KEM_DHKEM_P256:
-               return curve == GNUTLS_ECC_CURVE_SECP256R1;
-       case GNUTLS_HPKE_KEM_DHKEM_P384:
-               return curve == GNUTLS_ECC_CURVE_SECP384R1;
-       case GNUTLS_HPKE_KEM_DHKEM_P521:
-               return curve == GNUTLS_ECC_CURVE_SECP521R1;
-       case GNUTLS_HPKE_KEM_DHKEM_X25519:
-               return curve == GNUTLS_ECC_CURVE_X25519;
-       case GNUTLS_HPKE_KEM_DHKEM_X448:
-               return curve == GNUTLS_ECC_CURVE_X448;
-       default:
-               return 0;
-       }
-}
-
-static gnutls_pk_algorithm_t
-_gnutls_hpke_get_kem_associated_pk_algorithm(const gnutls_hpke_kem_t kem)
-{
-       switch (kem) {
-       case GNUTLS_HPKE_KEM_DHKEM_P256:
-       case GNUTLS_HPKE_KEM_DHKEM_P384:
-       case GNUTLS_HPKE_KEM_DHKEM_P521:
-               return GNUTLS_PK_EC;
-       case GNUTLS_HPKE_KEM_DHKEM_X25519:
-               return GNUTLS_PK_ECDH_X25519;
-       case GNUTLS_HPKE_KEM_DHKEM_X448:
-               return GNUTLS_PK_ECDH_X448;
-       default:
-               return GNUTLS_PK_UNKNOWN;
-       }
-}
-
-static gnutls_mac_algorithm_t _gnutls_kdf_to_mac(const gnutls_hpke_kdf_t kdf)
-{
-       switch (kdf) {
-       case GNUTLS_HPKE_KDF_HKDF_SHA256:
-               return GNUTLS_MAC_SHA256;
-       case GNUTLS_HPKE_KDF_HKDF_SHA384:
-               return GNUTLS_MAC_SHA384;
-       case GNUTLS_HPKE_KDF_HKDF_SHA512:
-               return GNUTLS_MAC_SHA512;
-       default:
-               return GNUTLS_MAC_UNKNOWN;
-       }
-}
-
-static gnutls_cipher_algorithm_t
-_gnutls_hpke_aead_to_cipher(const gnutls_hpke_aead_t aead)
-{
-       switch (aead) {
-       case GNUTLS_HPKE_AEAD_AES_128_GCM:
-               return GNUTLS_CIPHER_AES_128_GCM;
-       case GNUTLS_HPKE_AEAD_AES_256_GCM:
-               return GNUTLS_CIPHER_AES_256_GCM;
-       case GNUTLS_HPKE_AEAD_CHACHA20_POLY1305:
-               return GNUTLS_CIPHER_CHACHA20_POLY1305;
-       default:
-               return GNUTLS_CIPHER_UNKNOWN;
-       }
-}
-
-static int _gnutls_coord_pad_left(const gnutls_datum_t *in, const int out_size,
-                                 gnutls_datum_t *out)
-{
-       if ((int)in->size > out_size) {
-               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-       }
-
-       out->size = out_size;
-       out->data = gnutls_malloc(out->size);
-       if (out->data == NULL) {
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       memset(out->data, 0, out->size - in->size);
-       memcpy(out->data + (out->size - in->size), in->data, in->size);
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static int _gnutls_pubkey_to_datum(const gnutls_pubkey_t pk,
-                                  gnutls_datum_t *datum)
-{
-       int ret = 0;
-       gnutls_ecc_curve_t curve;
-       gnutls_datum_t x = { NULL, 0 };
-       gnutls_datum_t y = { NULL, 0 };
-       gnutls_datum_t x_padded = { NULL, 0 };
-       gnutls_datum_t y_padded = { NULL, 0 };
-
-       ret = gnutls_pubkey_export_ecc_raw2(pk, &curve, &x, &y,
-                                           GNUTLS_EXPORT_FLAG_NO_LZ);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (curve == GNUTLS_ECC_CURVE_X25519 ||
-           curve == GNUTLS_ECC_CURVE_X448) {
-               datum->size = x.size;
-               datum->data = gnutls_malloc(datum->size);
-               if (datum->data == NULL) {
-                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-                       goto cleanup;
-               }
-
-               memcpy(datum->data, x.data, x.size);
-               goto cleanup;
-       }
-
-       const int coord_size = gnutls_ecc_curve_get_size(curve);
-       ret = _gnutls_coord_pad_left(&x, coord_size, &x_padded);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = _gnutls_coord_pad_left(&y, coord_size, &y_padded);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       datum->size = 1 + x_padded.size + y_padded.size;
-       datum->data = gnutls_malloc(datum->size);
-       if (datum->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       datum->data[0] = 0x04;
-       memcpy(datum->data + 1, x_padded.data, x_padded.size);
-       memcpy(datum->data + 1 + x_padded.size, y_padded.data, y_padded.size);
-
-cleanup:
-       if (x.data != NULL) {
-               gnutls_free(x.data);
-       }
-
-       if (y.data != NULL) {
-               gnutls_free(y.data);
-       }
-
-       if (x_padded.data != NULL) {
-               gnutls_free(x_padded.data);
-       }
-
-       if (y_padded.data != NULL) {
-               gnutls_free(y_padded.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_extract_coordinates_from_pubkey_datum(
-       const gnutls_datum_t *datum, const gnutls_ecc_curve_t curve,
-       gnutls_datum_t *x, gnutls_datum_t *y)
-{
-       const size_t coord_size = gnutls_ecc_curve_get_size(curve);
-
-       if (curve == GNUTLS_ECC_CURVE_X25519 ||
-           curve == GNUTLS_ECC_CURVE_X448) {
-               if (datum->size != coord_size) {
-                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-               }
-
-               x->size = coord_size;
-               x->data = gnutls_malloc(coord_size);
-               if (x->data == NULL) {
-                       return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               }
-
-               memcpy(x->data, datum->data, coord_size);
-               y->size = 0;
-               y->data = NULL;
-
-               return GNUTLS_E_SUCCESS;
-       }
-
-       if (datum->size != 1 + 2 * coord_size || datum->data[0] != 0x04) {
-               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-       }
-
-       x->size = coord_size;
-       x->data = gnutls_malloc(coord_size);
-       if (x->data == NULL) {
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       memcpy(x->data, datum->data + 1, coord_size);
-
-       y->size = coord_size;
-       y->data = gnutls_malloc(coord_size);
-       if (y->data == NULL) {
-               gnutls_free(x->data);
-               x->data = NULL;
-               x->size = 0;
-               y->size = 0;
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       memcpy(y->data, datum->data + 1 + coord_size, coord_size);
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static int gnutls_datum_to_pubkey(const gnutls_ecc_curve_t curve,
-                                 const gnutls_datum_t *datum,
-                                 gnutls_pubkey_t *pk)
-{
-       int ret;
-
-       gnutls_datum_t x = { NULL, 0 };
-       gnutls_datum_t y = { NULL, 0 };
-
-       ret = _gnutls_extract_coordinates_from_pubkey_datum(datum, curve, &x,
-                                                           &y);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = gnutls_pubkey_init(pk);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = gnutls_pubkey_import_ecc_raw(*pk, curve, &x, &y);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-cleanup:
-       if (x.data != NULL) {
-               gnutls_free(x.data);
-       }
-
-       if (y.data != NULL) {
-               gnutls_free(y.data);
-       }
-
-       return ret;
-}
-
-static gnutls_datum_t _gnutls_compute_suite_id(const uint16_t kem_id)
-{
-       gnutls_datum_t suite_id = { NULL, 5 };
-       suite_id.data = gnutls_malloc(suite_id.size);
-       if (suite_id.data == NULL) {
-               suite_id.size = 0;
-               return suite_id;
-       }
-
-       suite_id.data[0] = 'K';
-       suite_id.data[1] = 'E';
-       suite_id.data[2] = 'M';
-       suite_id.data[3] = (kem_id >> 8) & 0xff;
-       suite_id.data[4] = kem_id & 0xff;
-       return suite_id;
-}
-
-static gnutls_datum_t _gnutls_hpke_get_ikm_label(const gnutls_datum_t *suite_id,
-                                                const gnutls_datum_t *dh)
-{
-       const char *label_prefix = "HPKE-v1";
-       const size_t label_prefix_len = strlen(label_prefix);
-       const char *label_suffix = "eae_prk";
-       const size_t label_suffix_len = strlen(label_suffix);
-
-       gnutls_datum_t ikm_label = { NULL, 0 };
-
-       ikm_label.size =
-               label_prefix_len + suite_id->size + label_suffix_len + dh->size;
-       ikm_label.data = gnutls_malloc(ikm_label.size);
-       if (ikm_label.data == NULL) {
-               ikm_label.size = 0;
-               return ikm_label;
-       }
-
-       size_t offset = 0;
-       memcpy(ikm_label.data + offset, label_prefix, label_prefix_len);
-       offset += label_prefix_len;
-       memcpy(ikm_label.data + offset, suite_id->data, suite_id->size);
-       offset += suite_id->size;
-       memcpy(ikm_label.data + offset, label_suffix, label_suffix_len);
-       offset += label_suffix_len;
-       memcpy(ikm_label.data + offset, dh->data, dh->size);
-
-       return ikm_label;
-}
-
-static int _gnutls_hpke_get_kem_context(const gnutls_hpke_mode_t mode,
-                                       const gnutls_pubkey_t receiver_pubkey,
-                                       const gnutls_pubkey_t sender_pubkey,
-                                       const gnutls_pubkey_t ephemeral_pubkey,
-                                       gnutls_datum_t *kem_context)
-{
-       int ret;
-       gnutls_datum_t pkR_raw = { NULL, 0 };
-       gnutls_datum_t pkS_raw = { NULL, 0 };
-       gnutls_datum_t pkE_raw = { NULL, 0 };
-
-       ret = _gnutls_pubkey_to_datum(ephemeral_pubkey, &pkE_raw);
-       if (ret != 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = _gnutls_pubkey_to_datum(receiver_pubkey, &pkR_raw);
-       if (ret != 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               ret = _gnutls_pubkey_to_datum(sender_pubkey, &pkS_raw);
-               if (ret != 0) {
-                       gnutls_assert_val(ret);
-                       goto cleanup;
-               }
-       }
-
-       kem_context->size = pkE_raw.size + pkR_raw.size + pkS_raw.size;
-       kem_context->data = gnutls_malloc(kem_context->size);
-       if (kem_context->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       size_t offset = 0;
-       memcpy(kem_context->data + offset, pkE_raw.data, pkE_raw.size);
-       offset += pkE_raw.size;
-       memcpy(kem_context->data + offset, pkR_raw.data, pkR_raw.size);
-       offset += pkR_raw.size;
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               if (pkS_raw.data == NULL) {
-                       ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-                       goto cleanup;
-               }
-               memcpy(kem_context->data + offset, pkS_raw.data, pkS_raw.size);
-       }
-
-cleanup:
-       if (pkE_raw.data) {
-               gnutls_free(pkE_raw.data);
-       }
-
-       if (pkR_raw.data) {
-               gnutls_free(pkR_raw.data);
-       }
-
-       if (pkS_raw.data) {
-               gnutls_free(pkS_raw.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_get_info_label(const gnutls_hpke_mode_t mode,
-                                      const gnutls_pubkey_t receiver_pubkey,
-                                      const gnutls_pubkey_t sender_pubkey,
-                                      const gnutls_pubkey_t ephemeral_pubkey,
-                                      const gnutls_datum_t suite_id,
-                                      const uint16_t Nsecret,
-                                      gnutls_datum_t *info_label)
-{
-       int ret;
-       gnutls_datum_t kem_context = { NULL, 0 };
-       ret = _gnutls_hpke_get_kem_context(mode, receiver_pubkey, sender_pubkey,
-                                          ephemeral_pubkey, &kem_context);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (kem_context.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-               goto cleanup;
-       }
-
-       const char Nsecret_bytes[2] = { (char)(Nsecret >> 8),
-                                       (char)(Nsecret & 0xff) };
-       const char *label_prefix = "HPKE-v1";
-       const size_t label_prefix_len = strlen(label_prefix);
-       const char *label_suffix = "shared_secret";
-       const size_t label_suffix_len = strlen(label_suffix);
-
-       size_t info_label_len = 2 + label_prefix_len + suite_id.size +
-                               label_suffix_len + kem_context.size;
-       info_label->size = info_label_len;
-       info_label->data = gnutls_malloc(info_label->size);
-       if (info_label->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       size_t offset = 0;
-       memcpy(info_label->data + offset, Nsecret_bytes, 2);
-       offset += 2;
-       memcpy(info_label->data + offset, label_prefix, label_prefix_len);
-       offset += label_prefix_len;
-       memcpy(info_label->data + offset, suite_id.data, suite_id.size);
-       offset += suite_id.size;
-       memcpy(info_label->data + offset, label_suffix, label_suffix_len);
-       offset += label_suffix_len;
-       memcpy(info_label->data + offset, kem_context.data, kem_context.size);
-
-cleanup:
-       if (kem_context.data != NULL) {
-               gnutls_free(kem_context.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_get_shared_secret(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_mode_t mode, const gnutls_pubkey_t receiver_pubkey,
-       const gnutls_pubkey_t sender_pubkey,
-       const gnutls_pubkey_t ephemeral_pubkey, const gnutls_datum_t dh,
-       gnutls_datum_t *shared_secret)
-{
-       int ret = 0;
-       gnutls_datum_t ikm_label = { NULL, 0 };
-       gnutls_datum_t salt = { NULL, 0 };
-       gnutls_datum_t eae_prk = { NULL, 0 };
-       gnutls_datum_t info_label = { NULL, 0 };
-       gnutls_datum_t suite_id = _gnutls_compute_suite_id(kem);
-       if (suite_id.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       const gnutls_mac_algorithm_t mac = _gnutls_kdf_to_mac(kdf);
-       if (mac == GNUTLS_MAC_UNKNOWN) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
-               goto cleanup;
-       }
-
-       const uint8_t Nh = gnutls_hmac_get_len(mac);
-       if (Nh == 0) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
-               goto cleanup;
-       }
-
-       salt.size = Nh;
-       salt.data = gnutls_malloc(salt.size);
-       if (salt.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       gnutls_memset(salt.data, 0, Nh);
-
-       ikm_label = _gnutls_hpke_get_ikm_label(&suite_id, &dh);
-       if (ikm_label.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       eae_prk.size = Nh;
-       eae_prk.data = gnutls_malloc(eae_prk.size);
-       if (eae_prk.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       ret = gnutls_hkdf_extract(mac, &ikm_label, &salt, eae_prk.data);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = _gnutls_hpke_get_info_label(mode, receiver_pubkey, sender_pubkey,
-                                         ephemeral_pubkey, suite_id, Nh,
-                                         &info_label);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       shared_secret->size = Nh;
-       shared_secret->data = gnutls_malloc(shared_secret->size);
-       if (shared_secret->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto error;
-       }
-
-       ret = gnutls_hkdf_expand(mac, &eae_prk, &info_label,
-                                shared_secret->data, Nh);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (shared_secret->data != NULL) {
-               gnutls_free(shared_secret->data);
-               shared_secret->data = NULL;
-               shared_secret->size = 0;
-       }
-
-cleanup:
-       if (salt.data != NULL) {
-               gnutls_free(salt.data);
-       }
-
-       if (ikm_label.data != NULL) {
-               gnutls_free(ikm_label.data);
-       }
-
-       if (suite_id.data != NULL) {
-               gnutls_free(suite_id.data);
-       }
-
-       if (eae_prk.data != NULL) {
-               gnutls_memset(eae_prk.data, 0, eae_prk.size);
-               gnutls_free(eae_prk.data);
-       }
-
-       if (info_label.data != NULL) {
-               gnutls_free(info_label.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_encap_get_dh(const gnutls_hpke_mode_t mode,
-                                    const gnutls_pubkey_t receiver_pubkey,
-                                    const gnutls_privkey_t ephemeral_privkey,
-                                    const gnutls_privkey_t sender_privkey,
-                                    gnutls_datum_t *dh)
-{
-       int ret = 0;
-       gnutls_datum_t dhE = { NULL, 0 };
-       gnutls_datum_t dhS = { NULL, 0 };
-
-       ret = gnutls_privkey_derive_secret(ephemeral_privkey, receiver_pubkey,
-                                          NULL, &dhE, 0);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               ret = gnutls_privkey_derive_secret(
-                       sender_privkey, receiver_pubkey, NULL, &dhS, 0);
-               if (ret < 0) {
-                       gnutls_assert_val(ret);
-                       goto cleanup;
-               }
-       }
-
-       dh->size = dhS.size + dhE.size;
-       dh->data = gnutls_malloc(dh->size);
-       if (dh->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       memcpy(dh->data, dhE.data, dhE.size);
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               memcpy(dh->data + dhE.size, dhS.data, dhS.size);
-       }
-
-cleanup:
-
-       if (dhS.data != NULL) {
-               gnutls_free(dhS.data);
-       }
-
-       if (dhE.data != NULL) {
-               gnutls_free(dhE.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_dhkem_encap(const gnutls_hpke_kem_t kem,
-                                   const gnutls_hpke_kdf_t kdf,
-                                   const gnutls_hpke_mode_t mode,
-                                   const gnutls_pubkey_t receiver_pubkey,
-                                   const gnutls_privkey_t sender_privkey,
-                                   gnutls_datum_t *enc,
-                                   gnutls_datum_t *shared_secret)
-{
-       int ret = 0;
-       gnutls_ecc_curve_t curve;
-       gnutls_privkey_t ephemeral_privkey = NULL;
-       gnutls_pubkey_t ephemeral_pubkey = NULL;
-       gnutls_pubkey_t sender_pubkey = NULL;
-       gnutls_datum_t dh = { NULL, 0 };
-
-       ret = gnutls_pubkey_export_ecc_raw(receiver_pubkey, &curve, NULL, NULL);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (!_gnutls_is_key_curve_type_compatible_with_param_dhkem(kem,
-                                                                  curve)) {
-               ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-               goto cleanup;
-       }
-
-       ret = gnutls_privkey_init(&ephemeral_privkey);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       const gnutls_pk_algorithm_t pk_algo =
-               _gnutls_hpke_get_kem_associated_pk_algorithm(kem);
-       if (pk_algo == GNUTLS_PK_UNKNOWN) {
-               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
-               goto cleanup;
-       }
-
-       ret = gnutls_privkey_generate(ephemeral_privkey, pk_algo,
-                                     GNUTLS_CURVE_TO_BITS(curve), 0);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = gnutls_pubkey_init(&ephemeral_pubkey);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = gnutls_pubkey_import_privkey(ephemeral_pubkey, ephemeral_privkey,
-                                          0, 0);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       ret = _gnutls_pubkey_to_datum(ephemeral_pubkey, enc);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = _gnutls_hpke_encap_get_dh(mode, receiver_pubkey,
-                                       ephemeral_privkey, sender_privkey, &dh);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = gnutls_pubkey_init(&sender_pubkey);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto error;
-       }
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               ret = gnutls_pubkey_import_privkey(sender_pubkey,
-                                                  sender_privkey, 0, 0);
-               if (ret < 0) {
-                       ret = gnutls_assert_val(ret);
-                       goto error;
-               }
-       }
-
-       ret = _gnutls_hpke_get_shared_secret(kem, kdf, mode, receiver_pubkey,
-                                            sender_pubkey, ephemeral_pubkey,
-                                            dh, shared_secret);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (enc != NULL && enc->data != NULL) {
-               gnutls_free(enc->data);
-               enc->data = NULL;
-               enc->size = 0;
-       }
-
-       if (shared_secret != NULL && shared_secret->data != NULL) {
-               gnutls_free(shared_secret->data);
-               shared_secret->data = NULL;
-               shared_secret->size = 0;
-       }
-
-cleanup:
-
-       if (ephemeral_pubkey != NULL) {
-               gnutls_pubkey_deinit(ephemeral_pubkey);
-       }
-
-       if (ephemeral_privkey != NULL) {
-               gnutls_privkey_deinit(ephemeral_privkey);
-       }
-
-       if (sender_pubkey != NULL) {
-               gnutls_pubkey_deinit(sender_pubkey);
-       }
-
-       if (dh.data != NULL) {
-               gnutls_memset(dh.data, 0, dh.size);
-               gnutls_free(dh.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_decap_get_dh(const gnutls_hpke_mode_t mode,
-                                    const gnutls_pubkey_t ephemeral_pubkey,
-                                    const gnutls_pubkey_t sender_pubkey,
-                                    const gnutls_privkey_t receiver_privkey,
-                                    gnutls_datum_t *dh)
-{
-       int ret;
-       gnutls_datum_t dhS = { NULL, 0 };
-       gnutls_datum_t dhE = { NULL, 0 };
-
-       ret = gnutls_privkey_derive_secret(receiver_privkey, ephemeral_pubkey,
-                                          NULL, &dhE, 0);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               ret = gnutls_privkey_derive_secret(
-                       receiver_privkey, sender_pubkey, NULL, &dhS, 0);
-               if (ret < 0) {
-                       gnutls_assert_val(ret);
-                       goto cleanup;
-               }
-       }
-
-       dh->size = dhE.size + dhS.size;
-       dh->data = gnutls_malloc(dh->size);
-       if (dh->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       memcpy(dh->data, dhE.data, dhE.size);
-
-       if (_gnutls_hpke_is_auth_mode(mode)) {
-               memcpy(dh->data + dhE.size, dhS.data, dhS.size);
-       }
-
-cleanup:
-       if (dhE.data != NULL) {
-               gnutls_free(dhE.data);
-       }
-
-       if (dhS.data != NULL) {
-               gnutls_free(dhS.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_dhkem_decap(const gnutls_hpke_kem_t kem,
-                                   const gnutls_hpke_kdf_t kdf,
-                                   const gnutls_hpke_mode_t mode,
-                                   const gnutls_privkey_t receiver_privkey,
-                                   const gnutls_pubkey_t sender_pubkey,
-                                   const gnutls_datum_t *enc,
-                                   gnutls_datum_t *shared_secret)
-{
-       int ret;
-
-       gnutls_datum_t dh = { NULL, 0 };
-       gnutls_pubkey_t receiver_pubkey = NULL;
-       gnutls_pubkey_t ephemeral_pubkey = NULL;
-       gnutls_ecc_curve_t curve;
-
-       ret = gnutls_privkey_export_ecc_raw(receiver_privkey, &curve, NULL,
-                                           NULL, NULL);
-       if (ret < 0) {
-               ret = gnutls_assert_val(ret);
-               goto error;
-       }
-
-       if (!_gnutls_is_key_curve_type_compatible_with_param_dhkem(kem,
-                                                                  curve)) {
-               ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-               goto error;
-       }
-
-       ret = gnutls_datum_to_pubkey(curve, enc, &ephemeral_pubkey);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = _gnutls_hpke_decap_get_dh(mode, ephemeral_pubkey, sender_pubkey,
-                                       receiver_privkey, &dh);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = gnutls_pubkey_init(&receiver_pubkey);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = gnutls_pubkey_import_privkey(receiver_pubkey, receiver_privkey, 0,
-                                          0);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       ret = _gnutls_hpke_get_shared_secret(kem, kdf, mode, receiver_pubkey,
-                                            sender_pubkey, ephemeral_pubkey,
-                                            dh, shared_secret);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (shared_secret != NULL && shared_secret->data != NULL) {
-               gnutls_free(shared_secret->data);
-       }
-
-cleanup:
-       if (dh.data != NULL) {
-               gnutls_memset(dh.data, 0, dh.size);
-               gnutls_free(dh.data);
-       }
-
-       if (receiver_pubkey != NULL) {
-               gnutls_pubkey_deinit(receiver_pubkey);
-       }
-
-       if (ephemeral_pubkey != NULL) {
-               gnutls_pubkey_deinit(ephemeral_pubkey);
-       }
-
-       return ret;
-}
-
-static gnutls_datum_t
-_gnutls_get_suite_id_for_scheduling(const uint16_t kdf_id,
-                                   const uint16_t aead_id)
-{
-       gnutls_datum_t suite_id = { NULL, 8 };
-       suite_id.data = gnutls_malloc(suite_id.size);
-       if (suite_id.data == NULL) {
-               suite_id.size = 0;
-               return suite_id;
-       }
-
-       suite_id.data[0] = 'H';
-       suite_id.data[1] = 'P';
-       suite_id.data[2] = 'K';
-       suite_id.data[3] = 'E';
-       suite_id.data[4] = (kdf_id >> 8) & 0xff;
-       suite_id.data[5] = kdf_id & 0xff;
-       suite_id.data[6] = (aead_id >> 8) & 0xff;
-       suite_id.data[7] = aead_id & 0xff;
-
-       return suite_id;
-}
-
-static gnutls_datum_t
-_gnutls_get_labeled_extract_key(const gnutls_datum_t *suite_id,
-                               const gnutls_datum_t *label,
-                               const gnutls_datum_t *ikm)
-{
-       gnutls_datum_t extract_key = { NULL, 0 };
-
-       gnutls_datum_t label_prefix = { (unsigned char *)"HPKE-v1",
-                                       strlen("HPKE-v1") };
-
-       extract_key.size =
-               label_prefix.size + suite_id->size + label->size + ikm->size;
-       extract_key.data = gnutls_malloc(extract_key.size);
-       if (extract_key.data == NULL) {
-               extract_key.size = 0;
-               return extract_key;
-       }
-
-       size_t offset = 0;
-       memcpy(extract_key.data + offset, label_prefix.data, label_prefix.size);
-       offset += label_prefix.size;
-       memcpy(extract_key.data + offset, suite_id->data, suite_id->size);
-       offset += suite_id->size;
-       memcpy(extract_key.data + offset, label->data, label->size);
-       offset += label->size;
-       memcpy(extract_key.data + offset, ikm->data, ikm->size);
-
-       return extract_key;
-}
-
-static int
-_gnutls_labeled_extract(const gnutls_mac_algorithm_t mac,
-                       const size_t hash_size, const gnutls_datum_t *suite_id,
-                       const gnutls_datum_t *salt, const gnutls_datum_t *label,
-                       const gnutls_datum_t *ikm, gnutls_datum_t *out)
-{
-       int ret;
-       gnutls_datum_t extract_key =
-               _gnutls_get_labeled_extract_key(suite_id, label, ikm);
-       if (extract_key.data == NULL) {
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       out->size = hash_size;
-       out->data = gnutls_malloc(out->size);
-       if (out->data == NULL) {
-               out->size = 0;
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       ret = gnutls_hkdf_extract(mac, &extract_key, salt, out->data);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-       }
-
-cleanup:
-
-       if (extract_key.data != NULL) {
-               gnutls_free(extract_key.data);
-       }
-
-       return ret;
-}
-
-static gnutls_datum_t
-_gnutls_get_key_context_for_scheduling(const uint8_t mode,
-                                      const gnutls_datum_t psk_id_hash,
-                                      const gnutls_datum_t info_hash)
-{
-       const size_t context_size = 1 + psk_id_hash.size + info_hash.size;
-       gnutls_datum_t key_schedule_context = { gnutls_malloc(context_size),
-                                               context_size };
-       if (key_schedule_context.data == NULL) {
-               key_schedule_context.size = 0;
-               return key_schedule_context;
-       }
-
-       size_t offset = 0;
-       key_schedule_context.data[offset] = mode;
-       offset += 1;
-       memcpy(key_schedule_context.data + offset, psk_id_hash.data,
-              psk_id_hash.size);
-       offset += psk_id_hash.size;
-       memcpy(key_schedule_context.data + offset, info_hash.data,
-              info_hash.size);
-
-       return key_schedule_context;
-}
-
-static int _gnutls_hpke_compute_expand_info(const gnutls_hpke_kdf_t kdf,
-                                           const gnutls_hpke_aead_t aead,
-                                           const gnutls_datum_t *label,
-                                           const gnutls_datum_t *context,
-                                           const size_t L,
-                                           gnutls_datum_t *expand_info)
-{
-       gnutls_datum_t suite_id = { NULL, 0 };
-
-       char cL[2] = { 0 };
-       cL[0] = (L >> 8) & 0xff;
-       cL[1] = L & 0xff;
-
-       gnutls_datum_t label_prefix = { (unsigned char *)"HPKE-v1",
-                                       strlen("HPKE-v1") };
-
-       suite_id = _gnutls_get_suite_id_for_scheduling(kdf, aead);
-       if (suite_id.data == NULL) {
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       expand_info->size = 2 + label_prefix.size + suite_id.size +
-                           label->size + context->size;
-       expand_info->data = gnutls_malloc(expand_info->size);
-       if (expand_info->data == NULL) {
-               if (suite_id.data != NULL) {
-                       gnutls_free(suite_id.data);
-               }
-               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-       }
-
-       size_t offset = 0;
-       memcpy(expand_info->data + offset, cL, 2);
-       offset += 2;
-       memcpy(expand_info->data + offset, label_prefix.data,
-              label_prefix.size);
-       offset += label_prefix.size;
-       memcpy(expand_info->data + offset, suite_id.data, suite_id.size);
-       offset += suite_id.size;
-       memcpy(expand_info->data + offset, label->data, label->size);
-       offset += label->size;
-       memcpy(expand_info->data + offset, context->data, context->size);
-
-       if (suite_id.data != NULL) {
-               gnutls_free(suite_id.data);
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static int _gnutls_labeled_expand(const gnutls_hpke_kdf_t kdf,
-                                 const gnutls_hpke_aead_t aead,
-                                 const gnutls_datum_t *secret,
-                                 const gnutls_datum_t *label,
-                                 const gnutls_datum_t *context, const size_t L,
-                                 gnutls_datum_t *out)
-{
-       int ret = 0;
-       gnutls_datum_t expand_info = { NULL, 0 };
-       ret = _gnutls_hpke_compute_expand_info(kdf, aead, label, context, L,
-                                              &expand_info);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       out->size = L;
-       out->data = gnutls_malloc(out->size);
-       if (out->data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto error;
-       }
-
-       const gnutls_mac_algorithm_t mac = _gnutls_kdf_to_mac(kdf);
-       if (mac == GNUTLS_MAC_UNKNOWN) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
-               goto error;
-       }
-
-       ret = gnutls_hkdf_expand(mac, secret, &expand_info, out->data, L);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (out != NULL && out->data != NULL) {
-               gnutls_free(out->data);
-               out->data = NULL;
-               out->size = 0;
-       }
-
-cleanup:
-       if (expand_info.data != NULL) {
-               gnutls_free(expand_info.data);
-       }
-
-       return ret;
-}
-
-static int _gnutls_hpke_schedule(
-       const gnutls_datum_t shared_secret, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const gnutls_hpke_mode_t mode,
-       const gnutls_datum_t *info, const gnutls_datum_t *psk,
-       const gnutls_datum_t *psk_id, gnutls_datum_t *key,
-       gnutls_datum_t *base_nonce, gnutls_datum_t *exporter_secret)
-{
-       int ret = 0;
-       gnutls_datum_t salt = { NULL, 0 };
-       gnutls_datum_t psk_id_hash = { NULL, 0 };
-       gnutls_datum_t info_hash = { NULL, 0 };
-       gnutls_datum_t key_schedule_context = { NULL, 0 };
-       gnutls_datum_t secret = { NULL, 0 };
-       gnutls_datum_t suite_id = { NULL, 0 };
-
-       const gnutls_mac_algorithm_t mac = _gnutls_kdf_to_mac(kdf);
-       if (mac == GNUTLS_MAC_UNKNOWN) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
-               goto cleanup;
-       }
-
-       const uint8_t Nh = gnutls_hmac_get_len(mac);
-       if (Nh == 0) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
-               goto cleanup;
-       }
-
-       salt.size = Nh;
-       salt.data = gnutls_malloc(salt.size);
-       if (salt.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       gnutls_memset(salt.data, 0, Nh);
-
-       suite_id = _gnutls_get_suite_id_for_scheduling(kdf, aead);
-       if (suite_id.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t psk_id_label = { (unsigned char *)"psk_id_hash",
-                                             strlen("psk_id_hash") };
-
-       ret = _gnutls_labeled_extract(mac, Nh, &suite_id, &salt, &psk_id_label,
-                                     psk_id, &psk_id_hash);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t info_label = { (unsigned char *)"info_hash",
-                                           strlen("info_hash") };
-
-       ret = _gnutls_labeled_extract(mac, Nh, &suite_id, &salt, &info_label,
-                                     info, &info_hash);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       key_schedule_context = _gnutls_get_key_context_for_scheduling(
-               mode, psk_id_hash, info_hash);
-       if (key_schedule_context.data == NULL) {
-               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t secret_label = { (unsigned char *)"secret",
-                                             strlen("secret") };
-
-       ret = _gnutls_labeled_extract(mac, Nh, &suite_id, &shared_secret,
-                                     &secret_label, psk, &secret);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       const gnutls_cipher_algorithm_t cipher =
-               _gnutls_hpke_aead_to_cipher(aead);
-
-       const size_t Nk = gnutls_cipher_get_key_size(cipher);
-       if (Nk == 0) {
-               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_TYPE);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t key_label = { (unsigned char *)"key",
-                                          strlen("key") };
-
-       ret = _gnutls_labeled_expand(kdf, aead, &secret, &key_label,
-                                    &key_schedule_context, Nk, key);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t base_nonce_label = { (unsigned char *)"base_nonce",
-                                                 strlen("base_nonce") };
-
-       const uint8_t Nn = 12;
-       ret = _gnutls_labeled_expand(kdf, aead, &secret, &base_nonce_label,
-                                    &key_schedule_context, Nn, base_nonce);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-       const gnutls_datum_t exporter_secret_label = {
-               (unsigned char *)"exporter_secret", strlen("exporter_secret")
-       };
-
-       ret = _gnutls_labeled_expand(kdf, aead, &secret, &exporter_secret_label,
-                                    &key_schedule_context, Nh,
-                                    exporter_secret);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto cleanup;
-       }
-
-cleanup:
-       if (salt.data != NULL) {
-               gnutls_free(salt.data);
-       }
-
-       if (psk_id_hash.data != NULL) {
-               gnutls_free(psk_id_hash.data);
-       }
-
-       if (info_hash.data != NULL) {
-               gnutls_free(info_hash.data);
-       }
-
-       if (key_schedule_context.data != NULL) {
-               gnutls_free(key_schedule_context.data);
-       }
-
-       if (secret.data != NULL) {
-               gnutls_memset(secret.data, 0, secret.size);
-               gnutls_free(secret.data);
-       }
-
-       if (suite_id.data != NULL) {
-               gnutls_free(suite_id.data);
-       }
-
-       return ret;
-}
-
-static int get_mode_from_encap_ctx(const gnutls_hpke_encap_context_t *ctx,
-                                  gnutls_hpke_mode_t *mode)
-{
-       *mode = GNUTLS_HPKE_MODE_BASE;
-
-       if (ctx->psk != NULL || ctx->psk_id != NULL) {
-               if (ctx->psk == NULL || ctx->psk_id == NULL) {
-                       _gnutls_debug_log(
-                               "HPKE: both psk and psk_id must be set for PSK modes\n");
-                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-               }
-               *mode = GNUTLS_HPKE_MODE_PSK;
-       }
-
-       if (ctx->sender_privkey != NULL) {
-               if (*mode == GNUTLS_HPKE_MODE_PSK) {
-                       *mode = GNUTLS_HPKE_MODE_AUTH_PSK;
-               } else {
-                       *mode = GNUTLS_HPKE_MODE_AUTH;
-               }
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-/**
- * gnutls_hpke_encap:
- * @ctx: The encapsulation context
- * @enc: Output encapsulated key
- * @key: Output key
- * @base_nonce: Output base nonce
- * @exporter_secret: Output exporter secret
- *
- * This function performs the HPKE encapsulation operation, deriving the
- * encapsulated key, key, base nonce and exporter secret.
- *
- * The HPKE mode is determined from the context parameters as follows:
- * - If bot psk and psk_id are set, then PSK mode is used.
- * - If sender_privkey is set, and both psk and psk_id are NULL, then Auth mode
- *   is used.
- * - If sender_privkey is set, and both psk and psk_id are set, then AuthPSK mode
- *   is used.
- * - Otherwise, Base mode is used.
- *
- * Returns: On success, GNUTLS_E_SUCCESS (0) is returned. On error, a
- * negative error value is returned.
- **/
-int gnutls_hpke_encap(const gnutls_hpke_encap_context_t *ctx,
-                     gnutls_datum_t *enc, gnutls_datum_t *key,
-                     gnutls_datum_t *base_nonce,
-                     gnutls_datum_t *exporter_secret)
-{
-       int ret = 0;
-       gnutls_datum_t shared_secret = { 0 };
-       const gnutls_datum_t *local_info = ctx->info ? ctx->info : &empty_datum;
-       const gnutls_datum_t *local_psk_id = ctx->psk_id ? ctx->psk_id :
-                                                          &empty_datum;
-       const gnutls_datum_t *local_psk = ctx->psk ? ctx->psk : &empty_datum;
-       gnutls_hpke_mode_t mode;
-       ret = get_mode_from_encap_ctx(ctx, &mode);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               return ret;
-       }
-
-       if (is_dhkem(ctx->kem)) {
-               ret = _gnutls_hpke_dhkem_encap(ctx->kem, ctx->kdf, mode,
-                                              ctx->receiver_pubkey,
-                                              ctx->sender_privkey, enc,
-                                              &shared_secret);
-               if (ret < 0) {
-                       gnutls_assert_val(ret);
-                       goto error;
-               }
-       } // TODO: else if(is_mlkem(ctx->kem)) {}
-       else {
-               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-       }
-
-       ret = _gnutls_hpke_schedule(shared_secret, ctx->kdf, ctx->aead, mode,
-                                   local_info, local_psk, local_psk_id, key,
-                                   base_nonce, exporter_secret);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (enc->data != NULL) {
-               gnutls_free(enc->data);
-               enc->data = NULL;
-               enc->size = 0;
-       }
-
-       if (key->data != NULL) {
-               gnutls_free(key->data);
-               key->data = NULL;
-               key->size = 0;
-       }
-
-       if (base_nonce->data != NULL) {
-               gnutls_free(base_nonce->data);
-               base_nonce->data = NULL;
-               base_nonce->size = 0;
-       }
-
-       if (exporter_secret->data != NULL) {
-               gnutls_free(exporter_secret->data);
-               exporter_secret->data = NULL;
-               exporter_secret->size = 0;
-       }
-
-cleanup:
-       if (shared_secret.data != NULL) {
-               gnutls_free(shared_secret.data);
-               shared_secret.data = NULL;
-               shared_secret.size = 0;
-       }
-
-       return ret;
-}
-
-static int get_mode_from_decap_ctx(const gnutls_hpke_decap_context_t *ctx,
-                                  gnutls_hpke_mode_t *mode)
-{
-       *mode = GNUTLS_HPKE_MODE_BASE;
-
-       if (ctx->psk != NULL || ctx->psk_id != NULL) {
-               if (ctx->psk == NULL || ctx->psk_id == NULL) {
-                       _gnutls_debug_log(
-                               "HPKE: both psk and psk_id must be set for PSK modes\n");
-                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-               }
-               *mode = GNUTLS_HPKE_MODE_PSK;
-       }
-
-       if (ctx->sender_pubkey != NULL) {
-               if (*mode == GNUTLS_HPKE_MODE_PSK) {
-                       *mode = GNUTLS_HPKE_MODE_AUTH_PSK;
-               } else {
-                       *mode = GNUTLS_HPKE_MODE_AUTH;
-               }
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-/**
- * gnutls_hpke_decap:
- * @ctx: The decapsulation context
- * @key: Output key
- * @base_nonce: Output base nonce
- * @exporter_secret: Output exporter secret
- *
- * This function performs the HPKE decapsulation operation, deriving the
- * key, base nonce and exporter secret.
- *
- * The HPKE mode is determined from the context parameters as follows:
- * - If bot psk and psk_id are set, then PSK mode is used.
- * - If sender_privkey is set, and both psk and psk_id are NULL, then Auth mode
- *   is used.
- * - If sender_privkey is set, and both psk and psk_id are set, then AuthPSK mode
- *   is used.
- * - Otherwise, Base mode is used.
- *
- * Returns: On success, GNUTLS_E_SUCCESS (0) is returned. On error, a
- * negative error value is returned.
- **/
-int gnutls_hpke_decap(const gnutls_hpke_decap_context_t *ctx,
-                     gnutls_datum_t *key, gnutls_datum_t *base_nonce,
-                     gnutls_datum_t *exporter_secret)
-{
-       int ret = 0;
-       gnutls_datum_t shared_secret = { NULL, 0 };
-       const gnutls_datum_t *local_info = ctx->info ? ctx->info : &empty_datum;
-       const gnutls_datum_t *local_psk_id = ctx->psk_id ? ctx->psk_id :
-                                                          &empty_datum;
-       const gnutls_datum_t *local_psk = ctx->psk ? ctx->psk : &empty_datum;
-       gnutls_hpke_mode_t mode;
-       ret = get_mode_from_decap_ctx(ctx, &mode);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               return ret;
-       }
-
-       if (is_dhkem(ctx->kem)) {
-               ret = _gnutls_hpke_dhkem_decap(ctx->kem, ctx->kdf, mode,
-                                              ctx->receiver_privkey,
-                                              ctx->sender_pubkey, ctx->enc,
-                                              &shared_secret);
-               if (ret < 0) {
-                       gnutls_assert_val(ret);
-                       goto error;
-               }
-       } // TODO: else if(is_mlkem(ctd->kem)) {}
-       else {
-               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
-       }
-
-       ret = _gnutls_hpke_schedule(shared_secret, ctx->kdf, ctx->aead, mode,
-                                   local_info, local_psk, local_psk_id, key,
-                                   base_nonce, exporter_secret);
-       if (ret < 0) {
-               gnutls_assert_val(ret);
-               goto error;
-       }
-
-       goto cleanup;
-
-error:
-       if (key->data != NULL) {
-               gnutls_free(key->data);
-               key->data = NULL;
-               key->size = 0;
-       }
-
-       if (base_nonce->data != NULL) {
-               gnutls_free(base_nonce->data);
-               base_nonce->data = NULL;
-               base_nonce->size = 0;
-       }
-
-       if (exporter_secret->data != NULL) {
-               gnutls_free(exporter_secret->data);
-               exporter_secret->data = NULL;
-               exporter_secret->size = 0;
-       }
-
-cleanup:
-       if (shared_secret.data != NULL) {
-               gnutls_free(shared_secret.data);
-       }
-       return ret;
-}
diff --git a/lib/hpke/helpers/hpke-builders.c b/lib/hpke/helpers/hpke-builders.c
new file mode 100644 (file)
index 0000000..6f96f44
--- /dev/null
@@ -0,0 +1,178 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "hpke-builders.h"
+
+#include <string.h>
+
+static const unsigned char hpke_v1_label[] = "HPKE-v1";
+static const unsigned char eae_prk_label[] = "eae_prk";
+static const unsigned char shared_secret_label[] = "shared_secret";
+
+static void _gnutls_hpke_append_buf(unsigned char *dst, size_t *dst_len,
+                                   const unsigned char *src, size_t src_len)
+{
+       memcpy(dst + *dst_len, src, src_len);
+       *dst_len += src_len;
+}
+
+void _gnutls_hpke_build_key_context_for_scheduling(
+       const uint8_t mode, const unsigned char *psk_id_hash,
+       const size_t psk_id_hash_len, const unsigned char *info_hash,
+       const size_t info_hash_len, unsigned char *key_schedule_context_buf,
+       size_t *key_schedule_context_len)
+{
+       *key_schedule_context_len = 0;
+
+       _gnutls_hpke_append_buf(key_schedule_context_buf,
+                               key_schedule_context_len, &mode, 1);
+       _gnutls_hpke_append_buf(key_schedule_context_buf,
+                               key_schedule_context_len, psk_id_hash,
+                               psk_id_hash_len);
+       _gnutls_hpke_append_buf(key_schedule_context_buf,
+                               key_schedule_context_len, info_hash,
+                               info_hash_len);
+}
+
+void _gnutls_hpke_build_expand_info(
+       const unsigned char *suite_id, const size_t suite_id_size,
+       const unsigned char *label, const size_t label_size,
+       const unsigned char *context, const size_t context_size, const size_t L,
+       unsigned char *expand_info_buf, size_t *expand_info_len)
+{
+       *expand_info_len = 0;
+
+       unsigned char L_buf[2];
+       L_buf[0] = (L >> 8) & 0xff;
+       L_buf[1] = L & 0xff;
+
+       _gnutls_hpke_append_buf(expand_info_buf, expand_info_len, L_buf, 2);
+       _gnutls_hpke_append_buf(expand_info_buf, expand_info_len, hpke_v1_label,
+                               sizeof(hpke_v1_label) - 1);
+       _gnutls_hpke_append_buf(expand_info_buf, expand_info_len, suite_id,
+                               suite_id_size);
+       _gnutls_hpke_append_buf(expand_info_buf, expand_info_len, label,
+                               label_size);
+       if (context != NULL) {
+               _gnutls_hpke_append_buf(expand_info_buf, expand_info_len,
+                                       context, context_size);
+       }
+}
+
+void _gnutls_hpke_build_labeled_extract_key(const unsigned char *suite_id,
+                                           const size_t suite_id_size,
+                                           const unsigned char *label,
+                                           const size_t label_size,
+                                           const gnutls_datum_t *ikm,
+                                           unsigned char *extract_key_buf,
+                                           size_t *extract_key_len)
+{
+       *extract_key_len = 0;
+
+       _gnutls_hpke_append_buf(extract_key_buf, extract_key_len, hpke_v1_label,
+                               sizeof(hpke_v1_label) - 1);
+       _gnutls_hpke_append_buf(extract_key_buf, extract_key_len, suite_id,
+                               suite_id_size);
+       _gnutls_hpke_append_buf(extract_key_buf, extract_key_len, label,
+                               label_size);
+       if (ikm != NULL) {
+               _gnutls_hpke_append_buf(extract_key_buf, extract_key_len,
+                                       ikm->data, ikm->size);
+       }
+}
+
+void _gnutls_hpke_build_kem_suite_id(const uint16_t kem_id,
+                                    unsigned char *suite_id)
+{
+       suite_id[0] = 'K';
+       suite_id[1] = 'E';
+       suite_id[2] = 'M';
+       suite_id[3] = (kem_id >> 8) & 0xff;
+       suite_id[4] = kem_id & 0xff;
+}
+
+void _gnutls_hpke_build_ikm_label(const unsigned char *suite_id,
+                                 const size_t suite_id_size,
+                                 const unsigned char *dh, const size_t dh_size,
+                                 unsigned char *ikm_label_buf,
+                                 size_t *ikm_label_len)
+{
+       *ikm_label_len = 0;
+
+       _gnutls_hpke_append_buf(ikm_label_buf, ikm_label_len, hpke_v1_label,
+                               sizeof(hpke_v1_label) - 1);
+       _gnutls_hpke_append_buf(ikm_label_buf, ikm_label_len, suite_id,
+                               suite_id_size);
+       _gnutls_hpke_append_buf(ikm_label_buf, ikm_label_len, eae_prk_label,
+                               sizeof(eae_prk_label) - 1);
+       _gnutls_hpke_append_buf(ikm_label_buf, ikm_label_len, dh, dh_size);
+}
+
+void _gnutls_hpke_build_info_label(
+       const unsigned char *receiver_pubkey_raw,
+       size_t receiver_pubkey_raw_size, const unsigned char *sender_pubkey_raw,
+       size_t sender_pubkey_raw_size,
+       const unsigned char *ephemeral_pubkey_raw,
+       size_t ephemeral_pubkey_raw_size, const unsigned char *suite_id,
+       const size_t suite_id_size, const uint16_t Nsecret,
+       unsigned char *info_label_buf, size_t *info_label_len)
+{
+       *info_label_len = 0;
+
+       unsigned char Nsecret_buf[2];
+       Nsecret_buf[0] = (Nsecret >> 8) & 0xff;
+       Nsecret_buf[1] = Nsecret & 0xff;
+
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len, Nsecret_buf, 2);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len, hpke_v1_label,
+                               sizeof(hpke_v1_label) - 1);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len, suite_id,
+                               suite_id_size);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len,
+                               shared_secret_label,
+                               sizeof(shared_secret_label) - 1);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len,
+                               ephemeral_pubkey_raw,
+                               ephemeral_pubkey_raw_size);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len,
+                               receiver_pubkey_raw, receiver_pubkey_raw_size);
+       _gnutls_hpke_append_buf(info_label_buf, info_label_len,
+                               sender_pubkey_raw, sender_pubkey_raw_size);
+}
+
+void _gnutls_hpke_build_suite_id_for_scheduling(const uint16_t kem_id,
+                                               const uint16_t kdf_id,
+                                               const uint16_t aead_id,
+                                               unsigned char *suite_id)
+{
+       suite_id[0] = 'H';
+       suite_id[1] = 'P';
+       suite_id[2] = 'K';
+       suite_id[3] = 'E';
+       suite_id[4] = (kem_id >> 8) & 0xff;
+       suite_id[5] = kem_id & 0xff;
+       suite_id[6] = (kdf_id >> 8) & 0xff;
+       suite_id[7] = kdf_id & 0xff;
+       suite_id[8] = (aead_id >> 8) & 0xff;
+       suite_id[9] = aead_id & 0xff;
+}
diff --git a/lib/hpke/helpers/hpke-builders.h b/lib/hpke/helpers/hpke-builders.h
new file mode 100644 (file)
index 0000000..4b87f94
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef HPKE_BUILDERS_HELPER_H
+#define HPKE_BUILDERS_HELPER_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <gnutls/gnutls.h>
+
+#include <stdint.h>
+
+void _gnutls_hpke_build_key_context_for_scheduling(
+       const uint8_t mode, const unsigned char *psk_id_hash,
+       const size_t psk_id_hash_len, const unsigned char *info_hash,
+       const size_t info_hash_len, unsigned char *key_schedule_context_buf,
+       size_t *key_schedule_context_len);
+
+void _gnutls_hpke_build_expand_info(
+       const unsigned char *suite_id, const size_t suite_id_size,
+       const unsigned char *label, const size_t label_size,
+       const unsigned char *context, const size_t context_size, const size_t L,
+       unsigned char *expand_info_buf, size_t *expand_info_len);
+
+void _gnutls_hpke_build_labeled_extract_key(const unsigned char *suite_id,
+                                           const size_t suite_id_size,
+                                           const unsigned char *label,
+                                           const size_t label_size,
+                                           const gnutls_datum_t *ikm,
+                                           unsigned char *extract_key_buf,
+                                           size_t *extract_key_len);
+
+void _gnutls_hpke_build_kem_suite_id(const uint16_t kem_id,
+                                    unsigned char *suite_id);
+
+void _gnutls_hpke_build_ikm_label(const unsigned char *suite_id,
+                                 const size_t suite_id_size,
+                                 const unsigned char *dh, const size_t dh_size,
+                                 unsigned char *ikm_label_buf,
+                                 size_t *ikm_label_len);
+
+void _gnutls_hpke_build_info_label(
+       const unsigned char *receiver_pubkey_raw,
+       size_t receiver_pubkey_raw_size, const unsigned char *sender_pubkey_raw,
+       size_t sender_pubkey_raw_size,
+       const unsigned char *ephemeral_pubkey_raw,
+       size_t ephemeral_pubkey_raw_size, const unsigned char *suite_id,
+       const size_t suite_id_size, const uint16_t Nsecret,
+       unsigned char *info_label_buf, size_t *info_label_len);
+
+void _gnutls_hpke_build_suite_id_for_scheduling(const uint16_t kem_id,
+                                               const uint16_t kdf_id,
+                                               const uint16_t aead_id,
+                                               unsigned char *suite_id);
+
+#endif /* HPKE_BUILDERS_HELPER_H */
diff --git a/lib/hpke/helpers/hpke-hkdf.c b/lib/hpke/helpers/hpke-hkdf.c
new file mode 100644 (file)
index 0000000..031d19b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "hpke-hkdf.h"
+#include "hpke-builders.h"
+
+#include "errors.h"
+
+#include <gnutls/crypto.h>
+
+#define GNUTLS_HPKE_MAX_EXTRACT_KEY_SIZE 94
+
+int _gnutls_hpke_labeled_extract(
+       const gnutls_mac_algorithm_t mac, const unsigned char *suite_id,
+       const size_t suite_id_size, const unsigned char *salt,
+       const size_t salt_size, const unsigned char *label,
+       const size_t label_size, const gnutls_datum_t *ikm,
+       unsigned char *hash_out_buf, size_t *hash_out_len)
+{
+       int ret;
+       unsigned char extract_key_buf[GNUTLS_HPKE_MAX_EXTRACT_KEY_SIZE] = { 0 };
+       size_t extract_key_size = 0;
+
+       size_t hash_size = gnutls_hmac_get_len(mac);
+       if (hash_size == 0) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
+
+       _gnutls_hpke_build_labeled_extract_key(suite_id, suite_id_size, label,
+                                              label_size, ikm, extract_key_buf,
+                                              &extract_key_size);
+
+       gnutls_datum_t extract_key = { extract_key_buf, extract_key_size };
+       gnutls_datum_t salt_datum = { (unsigned char *)salt, salt_size };
+       ret = gnutls_hkdf_extract(mac, &extract_key, &salt_datum, hash_out_buf);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+       }
+       *hash_out_len = hash_size;
+
+       gnutls_memset(extract_key_buf, 0, extract_key_size);
+
+       return ret;
+}
diff --git a/lib/hpke/helpers/hpke-hkdf.h b/lib/hpke/helpers/hpke-hkdf.h
new file mode 100644 (file)
index 0000000..cbe15f9
--- /dev/null
@@ -0,0 +1,40 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef HPKE_HKDF_HELPER_H
+#define HPKE_HKDF_HELPER_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <gnutls/gnutls.h>
+
+int _gnutls_hpke_labeled_extract(
+       const gnutls_mac_algorithm_t mac, const unsigned char *suite_id,
+       const size_t suite_id_size, const unsigned char *salt,
+       const size_t salt_size, const unsigned char *label,
+       const size_t label_size, const gnutls_datum_t *ikm,
+       unsigned char *hash_out_buf, size_t *hash_out_len);
+
+#endif /* HPKE_HKDF_HELPER_H */
diff --git a/lib/hpke/helpers/hpke-key-management.c b/lib/hpke/helpers/hpke-key-management.c
new file mode 100644 (file)
index 0000000..f92a125
--- /dev/null
@@ -0,0 +1,810 @@
+/*
+ * copyright © 2026 david dudas
+ *
+ * author: david dudas <david.dudas03@e-uvt.ro>
+ *
+ * this file is part of gnutls.
+ *
+ * the gnutls is free software; you can redistribute it and/or
+ * modify it under the terms of the gnu lesser general public license
+ * as published by the free software foundation; either version 2.1 of
+ * the license, or (at your option) any later version.
+ *
+ * this library 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
+ * lesser general public license for more details.
+
+ *
+ * you should have received a copy of the gnu lesser general public license
+ * along with this program.  if not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "hpke-key-management.h"
+#include "hpke-params.h"
+#include "hpke-builders.h"
+#include "hpke-hkdf.h"
+
+#include "errors.h"
+
+#include <nettle/curve25519.h>
+#include <nettle/curve448.h>
+#include <nettle/ecc.h>
+#include <nettle/ecc-curve.h>
+
+#define GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE 66
+#define GNUTLS_HPKE_MAX_MONTGOMERY_KEY_SIZE 56
+
+static const unsigned char dkp_prk_label[] = "dkp_prk";
+static const unsigned char sk_label[] = "sk";
+static const unsigned char candidate_label[] = "candidate";
+
+static const unsigned char p256_order[32] = {
+       0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xbc, 0xe6, 0xfa, 0xad, 0xa7, 0x17,
+       0x9e, 0x84, 0xf3, 0xb9, 0xca, 0xc2, 0xfc, 0x63, 0x25, 0x51
+};
+
+static const unsigned char p384_order[48] = {
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xc7, 0x63, 0x4d, 0x81, 0xf4, 0x37, 0x2d, 0xdf, 0x58, 0x1a, 0x0d, 0xb2,
+       0x48, 0xb0, 0xa7, 0x7a, 0xec, 0xec, 0x19, 0x6a, 0xcc, 0xc5, 0x29, 0x73
+};
+
+static const unsigned char p521_order[66] = {
+       0x01, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+       0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static void _gnutls_hpke_coord_pad_left_to_buf(const gnutls_datum_t *in,
+                                              size_t out_size,
+                                              unsigned char *out)
+{
+       gnutls_memset(out, 0, out_size - in->size);
+       memcpy(out + (out_size - in->size), in->data, in->size);
+}
+
+int _gnutls_hpke_pubkey_to_datum(const gnutls_pubkey_t pk,
+                                unsigned char *pubkey_raw,
+                                size_t *pubkey_raw_size)
+{
+       int ret;
+       gnutls_ecc_curve_t curve;
+       gnutls_datum_t x = { NULL, 0 };
+       gnutls_datum_t y = { NULL, 0 };
+
+       *pubkey_raw_size = 0;
+
+       ret = gnutls_pubkey_export_ecc_raw2(pk, &curve, &x, &y,
+                                           GNUTLS_EXPORT_FLAG_NO_LZ);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       if (curve == GNUTLS_ECC_CURVE_X25519 ||
+           curve == GNUTLS_ECC_CURVE_X448) {
+               if (x.data == NULL ||
+                   x.size > GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE) {
+                       ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+                       goto cleanup;
+               }
+
+               memcpy(pubkey_raw, x.data, x.size);
+               *pubkey_raw_size = x.size;
+               goto cleanup;
+       }
+
+       size_t coord_size = gnutls_ecc_curve_get_size(curve);
+       size_t total_size = 1 + 2 * coord_size;
+
+       if (coord_size == 0 || total_size > GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE) {
+               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+               goto cleanup;
+       }
+
+       _gnutls_hpke_coord_pad_left_to_buf(&x, coord_size, pubkey_raw + 1);
+       _gnutls_hpke_coord_pad_left_to_buf(&y, coord_size,
+                                          pubkey_raw + 1 + coord_size);
+
+       pubkey_raw[0] = 0x04;
+       *pubkey_raw_size = total_size;
+       ret = 0;
+
+cleanup:
+       if (x.data != NULL) {
+               gnutls_free(x.data);
+       }
+
+       if (y.data != NULL) {
+               gnutls_free(y.data);
+       }
+
+       return ret;
+}
+
+static int _gnutls_hpke_extract_coordinates_from_pubkey_datum(
+       const gnutls_ecc_curve_t curve, const gnutls_datum_t *datum,
+       unsigned char *x, size_t *x_size, unsigned char *y, size_t *y_size)
+{
+       const size_t coord_size = gnutls_ecc_curve_get_size(curve);
+
+       if (coord_size == 0 ||
+           coord_size > GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (curve == GNUTLS_ECC_CURVE_X25519 ||
+           curve == GNUTLS_ECC_CURVE_X448) {
+               if (datum->size != coord_size) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+
+               memcpy(x, datum->data, coord_size);
+               *x_size = coord_size;
+       } else {
+               if (datum->size != 1 + 2 * coord_size ||
+                   datum->data[0] != 0x04) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+
+               memcpy(x, datum->data + 1, coord_size);
+               *x_size = coord_size;
+               memcpy(y, datum->data + 1 + coord_size, coord_size);
+               *y_size = coord_size;
+       }
+
+       return 0;
+}
+
+int _gnutls_hpke_datum_to_pubkey(const gnutls_ecc_curve_t curve,
+                                const gnutls_datum_t *datum,
+                                gnutls_pubkey_t *pk)
+{
+       int ret;
+       unsigned char x[GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE];
+       size_t x_size = 0;
+       unsigned char y[GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE];
+       size_t y_size = 0;
+
+       ret = _gnutls_hpke_extract_coordinates_from_pubkey_datum(
+               curve, datum, x, &x_size, y, &y_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       ret = gnutls_pubkey_init(pk);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       gnutls_datum_t x_datum = { x, x_size };
+       gnutls_datum_t y_datum = { y, y_size };
+       ret = gnutls_pubkey_import_ecc_raw(*pk, curve, &x_datum, &y_datum);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               gnutls_pubkey_deinit(*pk);
+               *pk = NULL;
+               return ret;
+       }
+
+       return ret;
+}
+
+static void _gnutls_hpke_clamp_sk(const gnutls_hpke_kem_t kem,
+                                 unsigned char *sk_buf)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_X25519: {
+               sk_buf[0] &= 248;
+               sk_buf[31] &= 127;
+               sk_buf[31] |= 64;
+               break;
+       }
+       case GNUTLS_HPKE_KEM_DHKEM_X448: {
+               sk_buf[0] &= 252;
+               sk_buf[55] |= 128;
+               break;
+       }
+       default:
+               break;
+       }
+}
+
+static int
+_gnutls_hpke_derive_montgomery_curve_public_key(const gnutls_hpke_kem_t kem,
+                                               const gnutls_datum_t *priv_raw,
+                                               unsigned char *pub_raw)
+{
+       uint8_t k[GNUTLS_HPKE_MAX_MONTGOMERY_KEY_SIZE];
+
+       memcpy(k, priv_raw->data, priv_raw->size);
+       _gnutls_hpke_clamp_sk(kem, k);
+
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_X25519: {
+               static const uint8_t basepoint[32] = { 9 };
+               curve25519_mul(pub_raw, k, basepoint);
+       } break;
+       case GNUTLS_HPKE_KEM_DHKEM_X448: {
+               static const uint8_t basepoint[56] = { 5 };
+               curve448_mul(pub_raw, k, basepoint);
+       } break;
+       default:
+               break;
+       }
+
+       gnutls_memset(k, 0, sizeof(k));
+       return 0;
+}
+
+static int _gnutls_hpke_montgomery_curve_keypair_from_raw_privkey(
+       const gnutls_mac_algorithm_t mac, const gnutls_hpke_kem_t kem,
+       const gnutls_datum_t *dkp_prk, const gnutls_ecc_curve_t curve,
+       const unsigned char *suite_id, size_t suite_id_size,
+       gnutls_privkey_t *privkey, gnutls_pubkey_t *pubkey)
+{
+       int ret;
+       unsigned char
+               labeled_expand_info[GNUTLS_HPKE_MAX_LABELED_EXPAND_INFO_SIZE] = {
+                       0
+               };
+       size_t labeled_expand_info_size = 0;
+       unsigned char sk_buf[GNUTLS_HPKE_MAX_MONTGOMERY_KEY_SIZE] = { 0 };
+       size_t sk_size = 0;
+
+       sk_size = gnutls_ecc_curve_get_size(curve);
+       if (sk_size == 0) {
+               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+               goto cleanup;
+       }
+
+       _gnutls_hpke_build_expand_info(suite_id, suite_id_size, sk_label,
+                                      sizeof(sk_label) - 1, NULL, 0, sk_size,
+                                      labeled_expand_info,
+                                      &labeled_expand_info_size);
+       gnutls_datum_t labeled_expand_info_datum = { labeled_expand_info,
+                                                    labeled_expand_info_size };
+       ret = gnutls_hkdf_expand(mac, dkp_prk, &labeled_expand_info_datum,
+                                sk_buf, sk_size);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       _gnutls_hpke_clamp_sk(kem, sk_buf);
+       ret = gnutls_privkey_init(privkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       gnutls_datum_t k = { sk_buf, sk_size };
+       unsigned char pk_buf[32];
+
+       _gnutls_hpke_derive_montgomery_curve_public_key(kem, &k, pk_buf);
+
+       gnutls_datum_t x = { pk_buf, 32 };
+       ret = gnutls_privkey_import_ecc_raw(*privkey, curve, &x, NULL, &k);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       ret = gnutls_pubkey_init(pubkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       ret = gnutls_pubkey_import_ecc_raw(*pubkey, curve, &x, NULL);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       goto cleanup;
+
+error:
+       if (privkey != NULL && *privkey != NULL) {
+               gnutls_privkey_deinit(*privkey);
+               *privkey = NULL;
+       }
+
+       if (pubkey != NULL && *pubkey != NULL) {
+               gnutls_pubkey_deinit(*pubkey);
+               *pubkey = NULL;
+       }
+
+cleanup:
+
+       gnutls_memset(sk_buf, 0, sizeof(sk_buf));
+       gnutls_memset(labeled_expand_info, 0, sizeof(labeled_expand_info));
+
+       return ret;
+}
+
+static int _gnutls_hpke_is_all_zero(const unsigned char *buf, size_t len)
+{
+       size_t i;
+       unsigned char acc = 0;
+
+       for (i = 0; i < len; i++)
+               acc |= buf[i];
+
+       return acc == 0;
+}
+
+static const unsigned char *
+_gnutls_hpke_get_kem_order(const gnutls_hpke_kem_t kem)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+               return p256_order;
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+               return p384_order;
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+               return p521_order;
+       default:
+               return NULL;
+       }
+}
+
+static int _gnutls_hpke_be_lt(const unsigned char *a, const unsigned char *b,
+                             size_t len)
+{
+       size_t i;
+
+       for (i = 0; i < len; i++) {
+               if (a[i] < b[i])
+                       return 1;
+               if (a[i] > b[i])
+                       return 0;
+       }
+
+       return 0;
+}
+
+static int
+_gnutls_hpke_get_ecc_and_curve_len_for_curve(const gnutls_ecc_curve_t curve,
+                                            const struct ecc_curve **ecc,
+                                            size_t *coord_size)
+{
+       switch (curve) {
+       case GNUTLS_ECC_CURVE_SECP256R1:
+               *ecc = nettle_get_secp_256r1();
+               *coord_size = 32;
+               break;
+       case GNUTLS_ECC_CURVE_SECP384R1:
+               *ecc = nettle_get_secp_384r1();
+               *coord_size = 48;
+               break;
+       case GNUTLS_ECC_CURVE_SECP521R1:
+               *ecc = nettle_get_secp_521r1();
+               *coord_size = 66;
+               break;
+       default:
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       return 0;
+}
+
+static int _gnutls_hpke_export_pubkey_coordinate(const size_t coord_size,
+                                                mpz_t p, unsigned char *p_raw,
+                                                size_t *p_raw_size)
+{
+       unsigned char tmp[66];
+       size_t count = 0;
+
+       memset(tmp, 0, sizeof(tmp));
+       memset(p_raw, 0, coord_size);
+
+       mpz_export(tmp, &count, 1, 1, 1, 0, p);
+       if (count > coord_size) {
+               return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+       }
+
+       memcpy(p_raw + (coord_size - count), tmp, count);
+       *p_raw_size = coord_size;
+
+       gnutls_memset(tmp, 0, sizeof(tmp));
+
+       return 0;
+}
+
+static int _gnutls_hpke_derive_raw_public_key_for_prime_curve(
+       const gnutls_ecc_curve_t curve, const gnutls_datum_t *priv_raw,
+       unsigned char *x, size_t *x_len, unsigned char *y, size_t *y_len)
+{
+       int ret = 0;
+       const struct ecc_curve *ecc = NULL;
+       struct ecc_scalar s;
+       struct ecc_point p;
+       mpz_t k, px, py;
+       size_t coord_len = 0;
+       int scalar_inited = 0;
+       int point_inited = 0;
+       int mpz_inited = 0;
+
+       ret = _gnutls_hpke_get_ecc_and_curve_len_for_curve(curve, &ecc,
+                                                          &coord_len);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       *x_len = 0;
+       *y_len = 0;
+
+       mpz_init(k);
+       mpz_init(px);
+       mpz_init(py);
+       mpz_inited = 1;
+
+       mpz_import(k, priv_raw->size, 1, 1, 1, 0, priv_raw->data);
+
+       ecc_scalar_init(&s, ecc);
+       scalar_inited = 1;
+
+       if (!ecc_scalar_set(&s, k)) {
+               ret = gnutls_assert_val(GNUTLS_E_ILLEGAL_PARAMETER);
+               goto cleanup;
+       }
+
+       ecc_point_init(&p, ecc);
+       point_inited = 1;
+
+       ecc_point_mul_g(&p, &s);
+       ecc_point_get(&p, px, py);
+
+       ret = _gnutls_hpke_export_pubkey_coordinate(coord_len, px, x, x_len);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_export_pubkey_coordinate(coord_len, py, y, y_len);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = 0;
+
+cleanup:
+       if (point_inited)
+               ecc_point_clear(&p);
+       if (scalar_inited)
+               ecc_scalar_clear(&s);
+       if (mpz_inited) {
+               mpz_clear(k);
+               mpz_clear(px);
+               mpz_clear(py);
+       }
+
+       return ret;
+}
+
+static int _gnutls_hpke_prime_curve_keypair_from_raw_privkey(
+       const gnutls_mac_algorithm_t mac, const gnutls_hpke_kem_t kem,
+       const gnutls_datum_t *dkp_prk, const gnutls_ecc_curve_t curve,
+       const unsigned char *suite_id, size_t suite_id_size,
+       gnutls_privkey_t *privkey, gnutls_pubkey_t *pubkey)
+{
+       int ret;
+       unsigned char
+               labeled_expand_info[GNUTLS_HPKE_MAX_LABELED_EXPAND_INFO_SIZE] = {
+                       0
+               };
+       size_t labeled_expand_info_size = 0;
+       unsigned char sk_buf[GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE] = { 0 };
+       size_t sk_size = 0;
+
+       sk_size = gnutls_ecc_curve_get_size(curve);
+       if (sk_size == 0) {
+               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+               goto cleanup;
+       }
+
+       for (size_t counter = 0; counter < 256; counter++) {
+               _gnutls_hpke_build_expand_info(suite_id, suite_id_size,
+                                              candidate_label,
+                                              sizeof(candidate_label) - 1,
+                                              (unsigned char *)&counter, 1,
+                                              sk_size, labeled_expand_info,
+                                              &labeled_expand_info_size);
+               gnutls_datum_t labeled_expand_info_datum = {
+                       labeled_expand_info, labeled_expand_info_size
+               };
+
+               ret = gnutls_hkdf_expand(mac, dkp_prk,
+                                        &labeled_expand_info_datum, sk_buf,
+                                        sk_size);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+
+               if (kem == GNUTLS_HPKE_KEM_DHKEM_P521) {
+                       sk_buf[0] &= 0x01;
+               }
+
+               if (_gnutls_hpke_is_all_zero(sk_buf, sk_size)) {
+                       continue;
+               }
+
+               const unsigned char *order = _gnutls_hpke_get_kem_order(kem);
+               if (order == NULL) {
+                       ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+                       goto cleanup;
+               }
+
+               ret = _gnutls_hpke_be_lt(sk_buf, order, sk_size);
+               if (!ret) {
+                       continue;
+               }
+
+               ret = gnutls_privkey_init(privkey);
+               if (ret < 0) {
+                       ret = gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+
+               gnutls_datum_t k = { sk_buf, sk_size };
+               unsigned char x_buf[GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE];
+               size_t x_buf_len = 0;
+               unsigned char y_buf[GNUTLS_HPKE_MAX_RAW_KEY_COORDINATE_SIZE];
+               size_t y_buf_len = 0;
+               ret = _gnutls_hpke_derive_raw_public_key_for_prime_curve(
+                       curve, &k, x_buf, &x_buf_len, y_buf, &y_buf_len);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+
+               gnutls_datum_t x = { x_buf, x_buf_len };
+               gnutls_datum_t y = { y_buf, y_buf_len };
+               ret = gnutls_privkey_import_ecc_raw(*privkey, curve, &x, &y,
+                                                   &k);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+
+               ret = gnutls_pubkey_init(pubkey);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+
+               ret = gnutls_pubkey_import_ecc_raw(*pubkey, curve, &x, &y);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+
+               break;
+       }
+
+       goto cleanup;
+
+error:
+       if (privkey != NULL && *privkey != NULL) {
+               gnutls_privkey_deinit(*privkey);
+               *privkey = NULL;
+       }
+
+       if (pubkey != NULL && *pubkey != NULL) {
+               gnutls_pubkey_deinit(*pubkey);
+               *pubkey = NULL;
+       }
+
+cleanup:
+
+       gnutls_memset(sk_buf, 0, sizeof(sk_buf));
+       gnutls_memset(labeled_expand_info, 0, sizeof(labeled_expand_info));
+
+       return ret;
+}
+
+int _gnutls_hpke_keypair_from_ikm(const gnutls_hpke_kem_t kem,
+                                 const gnutls_datum_t *ikme,
+                                 gnutls_privkey_t *privkey,
+                                 gnutls_pubkey_t *pubkey)
+{
+       int ret;
+       unsigned char dkp_prk_buf[GNUTLS_HPKE_MAX_HASH_SIZE] = { 0 };
+       size_t dkp_prk_len = 0;
+
+       const gnutls_mac_algorithm_t mac = _gnutls_hpke_kem_to_mac(kem);
+       if (mac == GNUTLS_MAC_UNKNOWN) {
+               ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+               goto cleanup;
+       }
+
+       gnutls_ecc_curve_t curve = _gnutls_hpke_kem_to_curve(kem);
+       if (curve == GNUTLS_ECC_CURVE_INVALID) {
+               ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+               goto cleanup;
+       }
+
+       unsigned char suite_id_buf[GNUTLS_HPKE_SUITE_ID_SIZE] = { 0 };
+       _gnutls_hpke_build_kem_suite_id(kem, suite_id_buf);
+
+       ret = _gnutls_hpke_labeled_extract(mac, suite_id_buf,
+                                          GNUTLS_HPKE_SUITE_ID_SIZE, NULL, 0,
+                                          dkp_prk_label,
+                                          sizeof(dkp_prk_label) - 1, ikme,
+                                          dkp_prk_buf, &dkp_prk_len);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       gnutls_datum_t dkp_prk = { dkp_prk_buf, dkp_prk_len };
+
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_X448:
+       case GNUTLS_HPKE_KEM_DHKEM_X25519:
+               ret = _gnutls_hpke_montgomery_curve_keypair_from_raw_privkey(
+                       mac, kem, &dkp_prk, curve, suite_id_buf,
+                       GNUTLS_HPKE_SUITE_ID_SIZE, privkey, pubkey);
+               break;
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+               ret = _gnutls_hpke_prime_curve_keypair_from_raw_privkey(
+                       mac, kem, &dkp_prk, curve, suite_id_buf,
+                       GNUTLS_HPKE_SUITE_ID_SIZE, privkey, pubkey);
+               break;
+       default:
+               ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+cleanup:
+
+       gnutls_memset(dkp_prk_buf, 0, dkp_prk_len);
+
+       return ret;
+}
+
+static int _gnutls_hpke_generate_new_keypair(
+       const gnutls_ecc_curve_t curve, const gnutls_hpke_kem_t kem,
+       const gnutls_pk_algorithm_t pk_algo,
+       gnutls_privkey_t *ephemeral_privkey, gnutls_pubkey_t *ephemeral_pubkey)
+{
+       int ret;
+
+       const gnutls_ecc_curve_t kem_associated_curve =
+               _gnutls_hpke_kem_to_curve(kem);
+       if (curve != kem_associated_curve) {
+               ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               return ret;
+       }
+
+       ret = gnutls_privkey_generate(*ephemeral_privkey, pk_algo,
+                                     GNUTLS_CURVE_TO_BITS(curve), 0);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               return ret;
+       }
+
+       ret = gnutls_pubkey_init(ephemeral_pubkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               return ret;
+       }
+
+       ret = gnutls_pubkey_import_privkey(*ephemeral_pubkey,
+                                          *ephemeral_privkey, 0, 0);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               return ret;
+       }
+
+       return ret;
+}
+
+int _gnutls_hpke_generate_keypair(const gnutls_datum_t *ikme,
+                                 const gnutls_hpke_kem_t kem,
+                                 const gnutls_pubkey_t receiver_pubkey,
+                                 gnutls_privkey_t *ephemeral_privkey,
+                                 gnutls_pubkey_t *ephemeral_pubkey)
+{
+       int ret;
+       if (ikme == NULL) {
+               gnutls_ecc_curve_t curve;
+
+               const gnutls_pk_algorithm_t pk_algo =
+                       _gnutls_hpke_get_kem_associated_pk_algorithm(kem);
+               if (pk_algo == GNUTLS_PK_UNKNOWN) {
+                       ret = gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
+                       return ret;
+               }
+
+               ret = gnutls_pubkey_export_ecc_raw(receiver_pubkey, &curve,
+                                                  NULL, NULL);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       return ret;
+               }
+
+               ret = _gnutls_hpke_generate_new_keypair(curve, kem, pk_algo,
+                                                       ephemeral_privkey,
+                                                       ephemeral_pubkey);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       return ret;
+               }
+
+       } else {
+               ret = _gnutls_hpke_keypair_from_ikm(
+                       kem, ikme, ephemeral_privkey, ephemeral_pubkey);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       return ret;
+               }
+       }
+
+       return ret;
+}
+
+int _gnutls_hpke_privkey_clone(gnutls_privkey_t src, gnutls_privkey_t *dst)
+{
+       int ret;
+       gnutls_x509_privkey_t xkey = NULL;
+
+       ret = gnutls_privkey_export_x509(src, &xkey);
+       if (ret < 0)
+               return gnutls_assert_val(ret);
+
+       ret = gnutls_privkey_init(dst);
+       if (ret < 0) {
+               gnutls_x509_privkey_deinit(xkey);
+               return gnutls_assert_val(ret);
+       }
+
+       ret = gnutls_privkey_import_x509(*dst, xkey,
+                                        GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE);
+       if (ret < 0) {
+               gnutls_privkey_deinit(*dst);
+               *dst = NULL;
+               gnutls_x509_privkey_deinit(xkey);
+               return gnutls_assert_val(ret);
+       }
+
+       return 0;
+}
+
+int _gnutls_hpke_pubkey_clone(gnutls_pubkey_t src, gnutls_pubkey_t *dst)
+{
+       int ret;
+       gnutls_datum_t der = { NULL, 0 };
+
+       ret = gnutls_pubkey_export2(src, GNUTLS_X509_FMT_DER, &der);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       ret = gnutls_pubkey_init(dst);
+       if (ret < 0) {
+               gnutls_free(der.data);
+               return gnutls_assert_val(ret);
+       }
+
+       ret = gnutls_pubkey_import(*dst, &der, GNUTLS_X509_FMT_DER);
+       gnutls_free(der.data);
+
+       if (ret < 0) {
+               gnutls_pubkey_deinit(*dst);
+               *dst = NULL;
+               return gnutls_assert_val(ret);
+       }
+
+       return 0;
+}
diff --git a/lib/hpke/helpers/hpke-key-management.h b/lib/hpke/helpers/hpke-key-management.h
new file mode 100644 (file)
index 0000000..5c117be
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef HPKE_KEY_MANAGEMENT_HELPER_H
+#define HPKE_KEY_MANAGEMENT_HELPER_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include <gnutls/abstract.h>
+
+#define GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE 133
+#define GNUTLS_HPKE_MAX_HASH_SIZE 64
+#define GNUTLS_HPKE_SUITE_ID_SIZE 5
+#define GNUTLS_HPKE_MAX_LABELED_EXPAND_INFO_SIZE 158
+
+int _gnutls_hpke_pubkey_to_datum(const gnutls_pubkey_t pk,
+                                unsigned char *pubkey_raw,
+                                size_t *pubkey_raw_size);
+
+int _gnutls_hpke_datum_to_pubkey(const gnutls_ecc_curve_t curve,
+                                const gnutls_datum_t *datum,
+                                gnutls_pubkey_t *pk);
+
+int _gnutls_hpke_keypair_from_ikm(const gnutls_hpke_kem_t kem,
+                                 const gnutls_datum_t *ikme,
+                                 gnutls_privkey_t *privkey,
+                                 gnutls_pubkey_t *pubkey);
+
+int _gnutls_hpke_generate_keypair(const gnutls_datum_t *ikme,
+                                 const gnutls_hpke_kem_t kem,
+                                 const gnutls_pubkey_t receiver_pubkey,
+                                 gnutls_privkey_t *ephemeral_privkey,
+                                 gnutls_pubkey_t *ephemeral_pubkey);
+
+int _gnutls_hpke_privkey_clone(gnutls_privkey_t src, gnutls_privkey_t *dst);
+
+int _gnutls_hpke_pubkey_clone(gnutls_pubkey_t src, gnutls_pubkey_t *dst);
+
+#endif /* HPKE_KEY_MANAGEMENT_HELPER_H */
diff --git a/lib/hpke/helpers/hpke-params.c b/lib/hpke/helpers/hpke-params.c
new file mode 100644 (file)
index 0000000..12e5a7c
--- /dev/null
@@ -0,0 +1,120 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "hpke-params.h"
+
+int _gnutls_is_kem_dh(const gnutls_hpke_kem_t kem)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+       case GNUTLS_HPKE_KEM_DHKEM_X25519:
+       case GNUTLS_HPKE_KEM_DHKEM_X448:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+gnutls_ecc_curve_t _gnutls_hpke_kem_to_curve(const gnutls_hpke_kem_t kem)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+               return GNUTLS_ECC_CURVE_SECP256R1;
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+               return GNUTLS_ECC_CURVE_SECP384R1;
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+               return GNUTLS_ECC_CURVE_SECP521R1;
+       case GNUTLS_HPKE_KEM_DHKEM_X25519:
+               return GNUTLS_ECC_CURVE_X25519;
+       case GNUTLS_HPKE_KEM_DHKEM_X448:
+               return GNUTLS_ECC_CURVE_X448;
+       default:
+               return GNUTLS_ECC_CURVE_INVALID;
+       }
+}
+
+gnutls_pk_algorithm_t
+_gnutls_hpke_get_kem_associated_pk_algorithm(const gnutls_hpke_kem_t kem)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+               return GNUTLS_PK_EC;
+       case GNUTLS_HPKE_KEM_DHKEM_X25519:
+               return GNUTLS_PK_ECDH_X25519;
+       case GNUTLS_HPKE_KEM_DHKEM_X448:
+               return GNUTLS_PK_ECDH_X448;
+       default:
+               return GNUTLS_PK_UNKNOWN;
+       }
+}
+
+gnutls_mac_algorithm_t _gnutls_hpke_kdf_to_mac(const gnutls_hpke_kdf_t kdf)
+{
+       switch (kdf) {
+       case GNUTLS_HPKE_KDF_HKDF_SHA256:
+               return GNUTLS_MAC_SHA256;
+       case GNUTLS_HPKE_KDF_HKDF_SHA384:
+               return GNUTLS_MAC_SHA384;
+       case GNUTLS_HPKE_KDF_HKDF_SHA512:
+               return GNUTLS_MAC_SHA512;
+       default:
+               return GNUTLS_MAC_UNKNOWN;
+       }
+}
+
+gnutls_mac_algorithm_t _gnutls_hpke_kem_to_mac(const gnutls_hpke_kem_t kem)
+{
+       switch (kem) {
+       case GNUTLS_HPKE_KEM_DHKEM_P256:
+               return GNUTLS_MAC_SHA256;
+       case GNUTLS_HPKE_KEM_DHKEM_P384:
+               return GNUTLS_MAC_SHA384;
+       case GNUTLS_HPKE_KEM_DHKEM_P521:
+               return GNUTLS_MAC_SHA512;
+       case GNUTLS_HPKE_KEM_DHKEM_X25519:
+               return GNUTLS_MAC_SHA256;
+       case GNUTLS_HPKE_KEM_DHKEM_X448:
+               return GNUTLS_MAC_SHA512;
+       default:
+               return GNUTLS_MAC_UNKNOWN;
+       }
+}
+
+gnutls_cipher_algorithm_t
+_gnutls_hpke_aead_to_cipher(const gnutls_hpke_aead_t aead)
+{
+       switch (aead) {
+       case GNUTLS_HPKE_AEAD_AES_128_GCM:
+               return GNUTLS_CIPHER_AES_128_GCM;
+       case GNUTLS_HPKE_AEAD_AES_256_GCM:
+               return GNUTLS_CIPHER_AES_256_GCM;
+       case GNUTLS_HPKE_AEAD_CHACHA20_POLY1305:
+               return GNUTLS_CIPHER_CHACHA20_POLY1305;
+       default:
+               return GNUTLS_CIPHER_UNKNOWN;
+       }
+}
diff --git a/lib/hpke/helpers/hpke-params.h b/lib/hpke/helpers/hpke-params.h
new file mode 100644 (file)
index 0000000..bc41f79
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifndef HPKE_PARAMS_HELPER_H
+#define HPKE_PARAMS_HELPER_H
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "gnutls/abstract.h"
+
+int _gnutls_is_kem_dh(const gnutls_hpke_kem_t kem);
+
+gnutls_ecc_curve_t _gnutls_hpke_kem_to_curve(const gnutls_hpke_kem_t kem);
+
+gnutls_pk_algorithm_t
+_gnutls_hpke_get_kem_associated_pk_algorithm(const gnutls_hpke_kem_t kem);
+
+gnutls_mac_algorithm_t _gnutls_hpke_kdf_to_mac(const gnutls_hpke_kdf_t kdf);
+
+gnutls_mac_algorithm_t _gnutls_hpke_kem_to_mac(const gnutls_hpke_kem_t kem);
+
+gnutls_cipher_algorithm_t
+_gnutls_hpke_aead_to_cipher(const gnutls_hpke_aead_t aead);
+
+#endif /* HPKE_PARAMS_HELPER_H */
diff --git a/lib/hpke/hpke.c b/lib/hpke/hpke.c
new file mode 100644 (file)
index 0000000..cd477aa
--- /dev/null
@@ -0,0 +1,1712 @@
+/*
+ * Copyright © 2026 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif /* HAVE_CONFIG_H */
+
+#include "hpke/helpers/hpke-params.h"
+#include "hpke/helpers/hpke-builders.h"
+#include "hpke/helpers/hpke-key-management.h"
+#include "hpke/helpers/hpke-hkdf.h"
+
+#include "errors.h"
+
+static const unsigned char psk_id_hash_label[] = "psk_id_hash";
+static const unsigned char info_hash_label[] = "info_hash";
+static const unsigned char secret_hash_label[] = "secret";
+static const unsigned char key_expand_label[] = "key";
+static const unsigned char base_nonce_expand_label[] = "base_nonce";
+static const unsigned char exporter_secret_expand_label[] = "exp";
+static const unsigned char export_secret_label[] = "sec";
+
+#define GNUTLS_HPKE_MAX_PARAMETER_SIZE 66
+#define GNUTLS_HPKE_PSK_MIN_SIZE 32
+#define GNUTLS_SCHEDULING_SUITE_ID_SIZE 10
+#define GNUTLS_HPKE_IKM_LABEL_MAX_SIZE 256
+#define GNUTLS_HPKE_MAX_SALT_SIZE 64
+#define GNUTLS_HPKE_MAX_EAE_PRK_SIZE 64
+#define GNUTLS_HPKE_MAX_SHARED_SECRET_SIZE 64
+#define GNUTLS_HPKE_MAX_INFO_LABEL_SIZE 448
+#define GNUTLS_HPKE_MAX_DH_SIZE 132
+#define GNUTLS_HPKE_MAX_KEY_SCHEDULE_CONTEXT_SIZE \
+       1 + GNUTLS_HPKE_MAX_HASH_SIZE + GNUTLS_HPKE_MAX_HASH_SIZE
+#define GNUTLS_HPKE_MAX_NONCE_SIZE 12
+#define GNUTLS_HPKE_MAX_LABELED_EXPORT_INFO_MAX_SIZE \
+       22 + GNUTLS_HPKE_MAX_PARAMETER_SIZE
+
+struct gnutls_hpke_context_st {
+       gnutls_hpke_mode_t mode;
+       gnutls_hpke_role_t role;
+
+       gnutls_hpke_kem_t kem;
+       gnutls_hpke_kdf_t kdf;
+       gnutls_hpke_aead_t aead;
+
+       gnutls_datum_t *psk;
+       gnutls_datum_t *psk_id;
+
+       gnutls_datum_t *ikme;
+
+       gnutls_pubkey_t sender_pubkey;
+       gnutls_privkey_t sender_privkey;
+
+       gnutls_datum_t key;
+       gnutls_datum_t base_nonce;
+       gnutls_datum_t exporter_secret;
+       uint64_t seq;
+};
+
+static int _gnutls_hpke_is_auth_mode(const gnutls_hpke_mode_t mode)
+{
+       return mode == GNUTLS_HPKE_MODE_AUTH ||
+              mode == GNUTLS_HPKE_MODE_AUTH_PSK;
+}
+
+static int _gnutls_hpke_is_psk_mode(const gnutls_hpke_mode_t mode)
+{
+       return mode == GNUTLS_HPKE_MODE_PSK ||
+              mode == GNUTLS_HPKE_MODE_AUTH_PSK;
+}
+
+static int _gnutls_is_key_curve_type_compatible_with_param_dhkem(
+       const gnutls_hpke_kem_t kem, const gnutls_ecc_curve_t curve)
+{
+       const gnutls_ecc_curve_t expected_curve =
+               _gnutls_hpke_kem_to_curve(kem);
+       return curve == expected_curve;
+}
+
+static int _gnutls_hpke_validate_pubkey_for_kem(gnutls_pubkey_t pk,
+                                               gnutls_hpke_kem_t kem)
+{
+       int ret;
+       unsigned int bits = 0;
+       gnutls_pk_algorithm_t pk_algo;
+       gnutls_ecc_curve_t curve;
+
+       if (pk == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       pk_algo = gnutls_pubkey_get_pk_algorithm(pk, &bits);
+       if (pk_algo == GNUTLS_PK_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (pk_algo != _gnutls_hpke_get_kem_associated_pk_algorithm(kem)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = gnutls_pubkey_export_ecc_raw(pk, &curve, NULL, NULL);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       if (!_gnutls_is_key_curve_type_compatible_with_param_dhkem(kem,
+                                                                  curve)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       return 0;
+}
+
+static int _gnutls_hpke_validate_privkey_for_kem(gnutls_privkey_t sk,
+                                                gnutls_hpke_kem_t kem)
+{
+       int ret;
+       unsigned int bits = 0;
+       gnutls_pk_algorithm_t pk_algo;
+       gnutls_ecc_curve_t curve;
+
+       if (sk == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       pk_algo = gnutls_privkey_get_pk_algorithm(sk, &bits);
+       if (pk_algo == GNUTLS_PK_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (pk_algo != _gnutls_hpke_get_kem_associated_pk_algorithm(kem)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = gnutls_privkey_export_ecc_raw(sk, &curve, NULL, NULL, NULL);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       if (!_gnutls_is_key_curve_type_compatible_with_param_dhkem(kem,
+                                                                  curve)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       return 0;
+}
+
+static int _gnutls_hpke_get_shared_secret(
+       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
+       const gnutls_hpke_mode_t mode, const gnutls_pubkey_t receiver_pubkey,
+       const gnutls_pubkey_t sender_pubkey,
+       const gnutls_pubkey_t ephemeral_pubkey, const unsigned char *dh,
+       const size_t dh_size, unsigned char *shared_secret,
+       size_t *shared_secret_size)
+{
+       int ret = 0;
+       unsigned char receiver_pubkey_raw[GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE];
+       size_t receiver_pubkey_raw_size = 0;
+       unsigned char sender_pubkey_raw[GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE];
+       size_t sender_pubkey_raw_size = 0;
+       unsigned char ephemeral_pubkey_raw[GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE];
+       size_t ephemeral_pubkey_raw_size = 0;
+       unsigned char info_label[GNUTLS_HPKE_MAX_INFO_LABEL_SIZE] = { 0 };
+       size_t info_label_size = 0;
+
+       const gnutls_mac_algorithm_t mac = _gnutls_hpke_kdf_to_mac(kdf);
+       if (mac == GNUTLS_MAC_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
+
+       const uint8_t Nh = gnutls_hmac_get_len(mac);
+       if (Nh == 0) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
+
+       unsigned char suite_id[GNUTLS_HPKE_SUITE_ID_SIZE] = { 0 };
+       _gnutls_hpke_build_kem_suite_id(kem, suite_id);
+
+       unsigned char ikm_label[GNUTLS_HPKE_IKM_LABEL_MAX_SIZE];
+       size_t ikm_label_size = 0;
+       _gnutls_hpke_build_ikm_label(suite_id, GNUTLS_HPKE_SUITE_ID_SIZE, dh,
+                                    dh_size, ikm_label, &ikm_label_size);
+
+       gnutls_datum_t ikm_label_datum = { ikm_label, ikm_label_size };
+
+       unsigned char salt[GNUTLS_HPKE_MAX_SALT_SIZE] = { 0 };
+       gnutls_datum_t salt_datum = { salt, Nh };
+       unsigned char eae_prk[GNUTLS_HPKE_MAX_EAE_PRK_SIZE] = { 0 };
+
+       ret = gnutls_hkdf_extract(mac, &ikm_label_datum, &salt_datum, eae_prk);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_pubkey_to_datum(ephemeral_pubkey,
+                                          ephemeral_pubkey_raw,
+                                          &ephemeral_pubkey_raw_size);
+       if (ret != 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_pubkey_to_datum(receiver_pubkey, receiver_pubkey_raw,
+                                          &receiver_pubkey_raw_size);
+       if (ret != 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       if (_gnutls_hpke_is_auth_mode(mode)) {
+               ret = _gnutls_hpke_pubkey_to_datum(sender_pubkey,
+                                                  sender_pubkey_raw,
+                                                  &sender_pubkey_raw_size);
+               if (ret != 0) {
+                       gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+       }
+
+       _gnutls_hpke_build_info_label(
+               receiver_pubkey_raw, receiver_pubkey_raw_size,
+               sender_pubkey_raw, sender_pubkey_raw_size, ephemeral_pubkey_raw,
+               ephemeral_pubkey_raw_size, suite_id, GNUTLS_HPKE_SUITE_ID_SIZE,
+               Nh, info_label, &info_label_size);
+
+       gnutls_datum_t eae_prk_datum = { eae_prk, Nh };
+       gnutls_datum_t info_label_datum = { info_label, info_label_size };
+       *shared_secret_size = Nh;
+       ret = gnutls_hkdf_expand(mac, &eae_prk_datum, &info_label_datum,
+                                shared_secret, *shared_secret_size);
+       if (ret < 0) {
+               gnutls_memset(shared_secret, 0, *shared_secret_size);
+               *shared_secret_size = 0;
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+cleanup:
+
+       gnutls_memset(ikm_label, 0, ikm_label_size);
+       gnutls_memset(eae_prk, 0, Nh);
+       gnutls_memset(info_label, 0, info_label_size);
+       gnutls_memset(receiver_pubkey_raw, 0, receiver_pubkey_raw_size);
+       gnutls_memset(sender_pubkey_raw, 0, sender_pubkey_raw_size);
+       gnutls_memset(ephemeral_pubkey_raw, 0, ephemeral_pubkey_raw_size);
+
+       return ret;
+}
+
+static int _gnutls_hpke_encap_get_dh(const gnutls_hpke_mode_t mode,
+                                    const gnutls_pubkey_t receiver_pubkey,
+                                    const gnutls_privkey_t ephemeral_privkey,
+                                    const gnutls_privkey_t sender_privkey,
+                                    unsigned char *dh, size_t *dh_size)
+{
+       int ret = 0;
+       gnutls_datum_t dhE = { NULL, 0 };
+       gnutls_datum_t dhS = { NULL, 0 };
+
+       ret = gnutls_privkey_derive_secret(ephemeral_privkey, receiver_pubkey,
+                                          NULL, &dhE, 0);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       if (_gnutls_hpke_is_auth_mode(mode)) {
+               ret = gnutls_privkey_derive_secret(
+                       sender_privkey, receiver_pubkey, NULL, &dhS, 0);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+       }
+
+       memcpy(dh, dhE.data, dhE.size);
+       *dh_size = dhE.size;
+
+       if (_gnutls_hpke_is_auth_mode(mode)) {
+               memcpy(dh + dhE.size, dhS.data, dhS.size);
+               *dh_size += dhS.size;
+       }
+
+cleanup:
+       if (dhS.data != NULL) {
+               gnutls_memset(dhS.data, 0, dhS.size);
+               gnutls_free(dhS.data);
+       }
+
+       if (dhE.data != NULL) {
+               gnutls_memset(dhE.data, 0, dhE.size);
+               gnutls_free(dhE.data);
+       }
+
+       return ret;
+}
+
+static int _gnutls_hpke_dhkem_encap(const gnutls_hpke_context_t ctx,
+                                   const gnutls_pubkey_t receiver_pubkey,
+                                   gnutls_datum_t *enc,
+                                   unsigned char *shared_secret,
+                                   size_t *shared_secret_size)
+{
+       int ret = 0;
+       gnutls_privkey_t ephemeral_privkey = NULL;
+       gnutls_pubkey_t ephemeral_pubkey = NULL;
+       gnutls_pubkey_t sender_pubkey = NULL;
+       unsigned char dh[GNUTLS_HPKE_MAX_DH_SIZE];
+       size_t dh_size = 0;
+
+       ret = _gnutls_hpke_generate_keypair(ctx->ikme, ctx->kem,
+                                           receiver_pubkey, &ephemeral_privkey,
+                                           &ephemeral_pubkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       unsigned char pubkey_raw[GNUTLS_HPKE_MAX_DHKEM_PUBKEY_SIZE];
+       size_t pubkey_raw_size = 0;
+       ret = _gnutls_hpke_pubkey_to_datum(ephemeral_pubkey, pubkey_raw,
+                                          &pubkey_raw_size);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       enc->size = pubkey_raw_size;
+       enc->data = gnutls_malloc(enc->size);
+       if (enc->data == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto cleanup;
+       }
+
+       memcpy(enc->data, pubkey_raw, pubkey_raw_size);
+
+       ret = _gnutls_hpke_encap_get_dh(ctx->mode, receiver_pubkey,
+                                       ephemeral_privkey, ctx->sender_privkey,
+                                       dh, &dh_size);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto error;
+       }
+
+       if (_gnutls_hpke_is_auth_mode(ctx->mode)) {
+               ret = gnutls_pubkey_init(&sender_pubkey);
+               if (ret < 0) {
+                       ret = gnutls_assert_val(ret);
+                       goto error;
+               }
+               ret = gnutls_pubkey_import_privkey(sender_pubkey,
+                                                  ctx->sender_privkey, 0, 0);
+               if (ret < 0) {
+                       ret = gnutls_assert_val(ret);
+                       goto error;
+               }
+       }
+
+       ret = _gnutls_hpke_get_shared_secret(ctx->kem, ctx->kdf, ctx->mode,
+                                            receiver_pubkey, sender_pubkey,
+                                            ephemeral_pubkey, dh, dh_size,
+                                            shared_secret, shared_secret_size);
+       if (ret < 0) {
+               if (*shared_secret_size > 0) {
+                       gnutls_memset(shared_secret, 0, *shared_secret_size);
+                       *shared_secret_size = 0;
+               }
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       goto cleanup;
+
+error:
+       if (enc != NULL && enc->data != NULL) {
+               gnutls_free(enc->data);
+               enc->data = NULL;
+               enc->size = 0;
+       }
+
+cleanup:
+       gnutls_memset(dh, 0, dh_size);
+
+       gnutls_pubkey_deinit(ephemeral_pubkey);
+       gnutls_privkey_deinit(ephemeral_privkey);
+       gnutls_pubkey_deinit(sender_pubkey);
+
+       return ret;
+}
+
+static int _gnutls_hpke_decap_get_dh(const gnutls_hpke_mode_t mode,
+                                    const gnutls_pubkey_t ephemeral_pubkey,
+                                    const gnutls_pubkey_t sender_pubkey,
+                                    const gnutls_privkey_t receiver_privkey,
+                                    unsigned char *dh, size_t *dh_size)
+{
+       int ret;
+       gnutls_datum_t dhS = { NULL, 0 };
+       gnutls_datum_t dhE = { NULL, 0 };
+
+       ret = gnutls_privkey_derive_secret(receiver_privkey, ephemeral_pubkey,
+                                          NULL, &dhE, 0);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       if (_gnutls_hpke_is_auth_mode(mode)) {
+               ret = gnutls_privkey_derive_secret(
+                       receiver_privkey, sender_pubkey, NULL, &dhS, 0);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+       }
+
+       memcpy(dh, dhE.data, dhE.size);
+       *dh_size = dhE.size;
+
+       if (_gnutls_hpke_is_auth_mode(mode)) {
+               memcpy(dh + dhE.size, dhS.data, dhS.size);
+               *dh_size += dhS.size;
+       }
+
+cleanup:
+       if (dhE.data != NULL) {
+               gnutls_memset(dhE.data, 0, dhE.size);
+               gnutls_free(dhE.data);
+       }
+
+       if (dhS.data != NULL) {
+               gnutls_memset(dhS.data, 0, dhS.size);
+               gnutls_free(dhS.data);
+       }
+
+       return ret;
+}
+
+static int _gnutls_hpke_dhkem_decap(
+       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
+       const gnutls_hpke_mode_t mode, const gnutls_privkey_t receiver_privkey,
+       const gnutls_pubkey_t sender_pubkey, const gnutls_datum_t *enc,
+       unsigned char *shared_secret, size_t *shared_secret_size)
+{
+       int ret;
+
+       gnutls_pubkey_t receiver_pubkey = NULL;
+       gnutls_pubkey_t ephemeral_pubkey = NULL;
+       gnutls_ecc_curve_t curve;
+       unsigned char dh[GNUTLS_HPKE_MAX_DH_SIZE];
+       size_t dh_size = 0;
+
+       ret = gnutls_privkey_export_ecc_raw(receiver_privkey, &curve, NULL,
+                                           NULL, NULL);
+       if (ret < 0) {
+               ret = gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       if (!_gnutls_is_key_curve_type_compatible_with_param_dhkem(kem,
+                                                                  curve)) {
+               ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_datum_to_pubkey(curve, enc, &ephemeral_pubkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_validate_pubkey_for_kem(ephemeral_pubkey, kem);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_decap_get_dh(mode, ephemeral_pubkey, sender_pubkey,
+                                       receiver_privkey, dh, &dh_size);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = gnutls_pubkey_init(&receiver_pubkey);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = gnutls_pubkey_import_privkey(receiver_pubkey, receiver_privkey, 0,
+                                          0);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_get_shared_secret(kem, kdf, mode, receiver_pubkey,
+                                            sender_pubkey, ephemeral_pubkey,
+                                            dh, dh_size, shared_secret,
+                                            shared_secret_size);
+       if (ret < 0) {
+               if (*shared_secret_size > 0) {
+                       gnutls_memset(shared_secret, 0, *shared_secret_size);
+                       *shared_secret_size = 0;
+               }
+               gnutls_assert_val(ret);
+       }
+
+cleanup:
+       gnutls_memset(dh, 0, dh_size);
+
+       gnutls_pubkey_deinit(receiver_pubkey);
+       gnutls_pubkey_deinit(ephemeral_pubkey);
+
+       return ret;
+}
+
+static int _gnutls_hpke_schedule(const unsigned char *shared_secret,
+                                const size_t shared_secret_size,
+                                const gnutls_datum_t *info,
+                                gnutls_hpke_context_t ctx)
+{
+       int ret = 0;
+
+       unsigned char psk_id_hash[GNUTLS_HPKE_MAX_HASH_SIZE] = { 0 };
+       size_t psk_id_hash_size = 0;
+       unsigned char info_hash[GNUTLS_HPKE_MAX_HASH_SIZE] = { 0 };
+       size_t info_hash_size = 0;
+       unsigned char key_schedule_context
+               [GNUTLS_HPKE_MAX_KEY_SCHEDULE_CONTEXT_SIZE] = { 0 };
+       size_t key_schedule_context_size = 0;
+       unsigned char secret[GNUTLS_HPKE_MAX_HASH_SIZE] = { 0 };
+       size_t secret_size = 0;
+       unsigned char
+               labeled_expand_info[GNUTLS_HPKE_MAX_LABELED_EXPAND_INFO_SIZE] = {
+                       0
+               };
+       size_t labeled_expand_info_size = 0;
+
+       const gnutls_mac_algorithm_t mac = _gnutls_hpke_kdf_to_mac(ctx->kdf);
+       if (mac == GNUTLS_MAC_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
+
+       const uint8_t Nh = gnutls_hmac_get_len(mac);
+       if (Nh == 0) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_HASH_ALGORITHM);
+       }
+
+       unsigned char salt[GNUTLS_HPKE_MAX_SALT_SIZE] = { 0 };
+       unsigned char suite_id[GNUTLS_SCHEDULING_SUITE_ID_SIZE];
+       _gnutls_hpke_build_suite_id_for_scheduling(ctx->kem, ctx->kdf,
+                                                  ctx->aead, suite_id);
+
+       ret = _gnutls_hpke_labeled_extract(
+               mac, suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE, salt, Nh,
+               psk_id_hash_label, sizeof(psk_id_hash_label) - 1, ctx->psk_id,
+               psk_id_hash, &psk_id_hash_size);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       ret = _gnutls_hpke_labeled_extract(mac, suite_id,
+                                          GNUTLS_SCHEDULING_SUITE_ID_SIZE,
+                                          salt, Nh, info_hash_label,
+                                          sizeof(info_hash_label) - 1, info,
+                                          info_hash, &info_hash_size);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       _gnutls_hpke_build_key_context_for_scheduling(
+               ctx->mode, psk_id_hash, psk_id_hash_size, info_hash,
+               info_hash_size, key_schedule_context,
+               &key_schedule_context_size);
+
+       ret = _gnutls_hpke_labeled_extract(
+               mac, suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE, shared_secret,
+               shared_secret_size, secret_hash_label,
+               sizeof(secret_hash_label) - 1, ctx->psk, secret, &secret_size);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       gnutls_datum_t secret_datum = { secret, secret_size };
+
+       _gnutls_hpke_build_suite_id_for_scheduling(ctx->kem, ctx->kdf,
+                                                  ctx->aead, suite_id);
+       gnutls_datum_t expand_info = { NULL, 0 };
+
+       if (ctx->aead != GNUTLS_HPKE_AEAD_EXPORT_ONLY) {
+               const gnutls_cipher_algorithm_t cipher =
+                       _gnutls_hpke_aead_to_cipher(ctx->aead);
+
+               const size_t Nk = gnutls_cipher_get_key_size(cipher);
+               if (Nk == 0) {
+                       ret = gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_TYPE);
+                       goto cleanup;
+               }
+
+               ctx->key.data = gnutls_malloc(Nk);
+               if (ctx->key.data == NULL) {
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto cleanup;
+               }
+               ctx->key.size = Nk;
+
+               _gnutls_hpke_build_expand_info(
+                       suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE,
+                       key_expand_label, sizeof(key_expand_label) - 1,
+                       key_schedule_context, key_schedule_context_size, Nk,
+                       labeled_expand_info, &labeled_expand_info_size);
+               expand_info.data = labeled_expand_info;
+               expand_info.size = labeled_expand_info_size;
+
+               ret = gnutls_hkdf_expand(mac, &secret_datum, &expand_info,
+                                        ctx->key.data, ctx->key.size);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+
+               const uint8_t Nn = gnutls_cipher_get_iv_size(cipher);
+
+               ctx->base_nonce.data = gnutls_malloc(Nn);
+               if (ctx->base_nonce.data == NULL) {
+                       ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+                       goto error;
+               }
+               ctx->base_nonce.size = Nn;
+
+               _gnutls_hpke_build_expand_info(
+                       suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE,
+                       base_nonce_expand_label,
+                       sizeof(base_nonce_expand_label) - 1,
+                       key_schedule_context, key_schedule_context_size, Nn,
+                       labeled_expand_info, &labeled_expand_info_size);
+               expand_info.data = labeled_expand_info;
+               expand_info.size = labeled_expand_info_size;
+               ret = gnutls_hkdf_expand(mac, &secret_datum, &expand_info,
+                                        ctx->base_nonce.data,
+                                        ctx->base_nonce.size);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+       }
+
+       ctx->exporter_secret.data = gnutls_malloc(Nh);
+       if (ctx->exporter_secret.data == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+       ctx->exporter_secret.size = Nh;
+
+       _gnutls_hpke_build_expand_info(
+               suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE,
+               exporter_secret_expand_label,
+               sizeof(exporter_secret_expand_label) - 1, key_schedule_context,
+               key_schedule_context_size, Nh, labeled_expand_info,
+               &labeled_expand_info_size);
+       expand_info.data = labeled_expand_info;
+       expand_info.size = labeled_expand_info_size;
+       ret = gnutls_hkdf_expand(mac, &secret_datum, &expand_info,
+                                ctx->exporter_secret.data,
+                                ctx->exporter_secret.size);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       return ret;
+
+error:
+       if (ctx->key.data != NULL) {
+               gnutls_memset(ctx->key.data, 0, ctx->key.size);
+               gnutls_free(ctx->key.data);
+               ctx->key.data = NULL;
+               ctx->key.size = 0;
+       }
+
+       if (ctx->base_nonce.data != NULL) {
+               gnutls_memset(ctx->base_nonce.data, 0, ctx->base_nonce.size);
+               gnutls_free(ctx->base_nonce.data);
+               ctx->base_nonce.data = NULL;
+               ctx->base_nonce.size = 0;
+       }
+
+       if (ctx->exporter_secret.data != NULL) {
+               gnutls_memset(ctx->exporter_secret.data, 0,
+                             ctx->exporter_secret.size);
+               gnutls_free(ctx->exporter_secret.data);
+               ctx->exporter_secret.data = NULL;
+               ctx->exporter_secret.size = 0;
+       }
+cleanup:
+
+       gnutls_memset(psk_id_hash, 0, psk_id_hash_size);
+       gnutls_memset(info_hash, 0, info_hash_size);
+       gnutls_memset(secret, 0, secret_size);
+       gnutls_memset(key_schedule_context, 0, key_schedule_context_size);
+       gnutls_memset(labeled_expand_info, 0, labeled_expand_info_size);
+
+       return ret;
+}
+
+/**
+ * gnutls_hpke_context_init:
+ * @ctx: A pointer to the HPKE context to initialize.
+ * @mode: The HPKE mode to use (Base, PSK, Auth, or AuthPSK).
+ * @role: The role of the context (Sender or Receiver).
+ * @kem: The KEM algorithm to use (e.g., DHKEM(X25519)).
+ * @kdf: The KDF algorithm to use (e.g., HKDF-SHA256).
+ * @aead: The AEAD algorithm to use (e.g., AES-128-GCM).
+ * This function initializes the HPKE context with the specified parameters.
+ * It allocates memory for the context and sets the initial values for the fields based on the provided parameters.
+ * The context must be deinitialized using gnutls_hpke_context_deinit() when it
+ * is no longer needed to free any allocated resources and securely erase sensitive information.
+ * Returns: 0 on success, or a negative error code on failure
+ */
+int gnutls_hpke_context_init(gnutls_hpke_context_t *ctx,
+                            const gnutls_hpke_mode_t mode,
+                            const gnutls_hpke_role_t role,
+                            const gnutls_hpke_kem_t kem,
+                            const gnutls_hpke_kdf_t kdf,
+                            const gnutls_hpke_aead_t aead)
+{
+       if (ctx == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       *ctx = gnutls_malloc(sizeof(**ctx));
+       if (*ctx == NULL) {
+               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+       }
+
+       (*ctx)->mode = mode;
+       (*ctx)->role = role;
+
+       (*ctx)->kem = kem;
+       (*ctx)->kdf = kdf;
+       (*ctx)->aead = aead;
+
+       (*ctx)->psk = NULL;
+       (*ctx)->psk_id = NULL;
+
+       (*ctx)->ikme = NULL;
+
+       (*ctx)->sender_pubkey = NULL;
+       (*ctx)->sender_privkey = NULL;
+
+       (*ctx)->key.data = NULL;
+       (*ctx)->key.size = 0;
+       (*ctx)->base_nonce.data = NULL;
+       (*ctx)->base_nonce.size = 0;
+       (*ctx)->exporter_secret.data = NULL;
+       (*ctx)->exporter_secret.size = 0;
+       (*ctx)->seq = 0;
+
+       return 0;
+}
+
+/**
+ * gnutls_hpke_context_deinit:
+ * @ctx: The HPKE context to deinitialize.
+ *
+ * This function deinitializes the HPKE context and securely erases any
+ * sensitive information contained within it, such as keys and secrets.
+ * It is important to call this function when the HPKE context is no longer needed
+ * to prevent sensitive data from lingering in memory.
+ * Returns: 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_context_deinit(gnutls_hpke_context_t ctx)
+{
+       if (ctx == NULL) {
+               return 0;
+       }
+
+       if (ctx->psk != NULL) {
+               if (ctx->psk->data != NULL) {
+                       gnutls_memset(ctx->psk->data, 0, ctx->psk->size);
+                       gnutls_free(ctx->psk->data);
+                       ctx->psk->data = NULL;
+                       ctx->psk->size = 0;
+               }
+               gnutls_free(ctx->psk);
+       }
+
+       if (ctx->psk_id != NULL) {
+               if (ctx->psk_id->data != NULL) {
+                       gnutls_memset(ctx->psk_id->data, 0, ctx->psk_id->size);
+                       gnutls_free(ctx->psk_id->data);
+                       ctx->psk_id->data = NULL;
+                       ctx->psk_id->size = 0;
+               }
+               gnutls_free(ctx->psk_id);
+       }
+
+       if (ctx->key.data != NULL) {
+               gnutls_memset(ctx->key.data, 0, ctx->key.size);
+               gnutls_free(ctx->key.data);
+       }
+
+       if (ctx->base_nonce.data != NULL) {
+               gnutls_memset(ctx->base_nonce.data, 0, ctx->base_nonce.size);
+               gnutls_free(ctx->base_nonce.data);
+       }
+
+       if (ctx->exporter_secret.data != NULL) {
+               gnutls_memset(ctx->exporter_secret.data, 0,
+                             ctx->exporter_secret.size);
+               gnutls_free(ctx->exporter_secret.data);
+       }
+
+       gnutls_pubkey_deinit(ctx->sender_pubkey);
+       gnutls_privkey_deinit(ctx->sender_privkey);
+
+       if (ctx->ikme != NULL) {
+               if (ctx->ikme->data != NULL) {
+                       gnutls_memset(ctx->ikme->data, 0, ctx->ikme->size);
+                       gnutls_free(ctx->ikme->data);
+                       ctx->ikme->data = NULL;
+                       ctx->ikme->size = 0;
+               }
+
+               gnutls_free(ctx->ikme);
+               ctx->ikme = NULL;
+       }
+
+       gnutls_free(ctx);
+       return 0;
+}
+
+/**
+ * gnutls_hpke_context_set_psk:
+ * @ctx: The HPKE context to set the PSK for.
+ * @psk: A pointer to a gnutls_datum_t structure containing the PSK value and its size.
+ * @psk_id: A pointer to a gnutls_datum_t structure containing the PSK identifier and its size.
+ *
+ * This function sets the PSK and its identifier in the HPKE context. 
+ * It securely erases any existing PSK and PSK identifier in the context before setting the new values.
+ * The function checks that the provided PSK and PSK identifier are valid and that the context is in
+ * a mode that supports PSKs.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_context_set_psk(gnutls_hpke_context_t ctx,
+                               const gnutls_datum_t *psk,
+                               const gnutls_datum_t *psk_id)
+{
+       if (ctx == NULL || psk == NULL || psk_id == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (!_gnutls_hpke_is_psk_mode(ctx->mode)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (psk->size < GNUTLS_HPKE_PSK_MIN_SIZE ||
+           psk->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (psk_id->size == 0 ||
+           psk_id->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       int ret = 0;
+
+       if (ctx->psk != NULL) {
+               gnutls_memset(ctx->psk->data, 0, ctx->psk->size);
+               gnutls_free(ctx->psk->data);
+               ctx->psk->data = NULL;
+               ctx->psk->size = 0;
+               gnutls_free(ctx->psk);
+               ctx->psk = NULL;
+       }
+
+       if (ctx->psk_id != NULL) {
+               gnutls_memset(ctx->psk_id->data, 0, ctx->psk_id->size);
+               gnutls_free(ctx->psk_id->data);
+               ctx->psk_id->data = NULL;
+               ctx->psk_id->size = 0;
+               gnutls_free(ctx->psk_id);
+               ctx->psk_id = NULL;
+       }
+
+       ctx->psk = gnutls_malloc(sizeof(*ctx->psk));
+       if (ctx->psk == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+
+       ctx->psk_id = gnutls_malloc(sizeof(*ctx->psk_id));
+       if (ctx->psk_id == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+
+       ctx->psk->size = psk->size;
+       ctx->psk->data = gnutls_malloc(ctx->psk->size);
+       if (ctx->psk->data == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+       memcpy(ctx->psk->data, psk->data, psk->size);
+
+       ctx->psk_id->size = psk_id->size;
+       ctx->psk_id->data = gnutls_malloc(ctx->psk_id->size);
+       if (ctx->psk_id->data == NULL) {
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto error;
+       }
+       memcpy(ctx->psk_id->data, psk_id->data, psk_id->size);
+
+       return ret;
+error:
+       if (ctx->psk != NULL) {
+               if (ctx->psk->data != NULL) {
+                       gnutls_memset(ctx->psk->data, 0, ctx->psk->size);
+                       gnutls_free(ctx->psk->data);
+                       ctx->psk->data = NULL;
+                       ctx->psk->size = 0;
+               }
+
+               gnutls_free(ctx->psk);
+               ctx->psk = NULL;
+       }
+
+       if (ctx->psk_id != NULL) {
+               if (ctx->psk_id->data != NULL) {
+                       gnutls_memset(ctx->psk_id->data, 0, ctx->psk_id->size);
+                       gnutls_free(ctx->psk_id->data);
+                       ctx->psk_id->data = NULL;
+                       ctx->psk_id->size = 0;
+               }
+
+               gnutls_free(ctx->psk_id);
+               ctx->psk_id = NULL;
+       }
+
+       return ret;
+}
+
+/**
+ * gnutls_hpke_context_set_sender_privkey:
+ * @ctx: The HPKE context to set the sender's private key for.
+ * @sender_privkey: The sender's private key to set in the context.
+ *
+ * This function should be used by the sender in authenticated modes (Auth and AuthPSK) to set their private key in the
+ * HPKE context.
+ *
+ * This function sets the sender's private key in the HPKE context. It securely erases any existing sender's private key
+ * in the context before setting the new value. The function checks that the provided sender's private key is valid and
+ * that the context is in a mode that supports authentication and that the role of the context is Sender.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_context_set_sender_privkey(gnutls_hpke_context_t ctx,
+                                          gnutls_privkey_t sender_privkey)
+{
+       if (ctx == NULL || sender_privkey == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (!_gnutls_hpke_is_auth_mode(ctx->mode)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_SENDER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       gnutls_privkey_deinit(ctx->sender_privkey);
+       ctx->sender_privkey = NULL;
+
+       return _gnutls_hpke_privkey_clone(sender_privkey, &ctx->sender_privkey);
+}
+
+/**
+ * gnutls_hpke_context_set_sender_pubkey:
+ * @ctx: The HPKE context to set the sender's public key for.
+ * @sender_pubkey: The sender's public key to set in the context.
+ *
+ * This function should be used by the receiver in authenticated modes (Auth and AuthPSK) to set the sender's public key
+ * in the HPKE context.
+ *
+ * This function sets the sender's public key in the HPKE context. It securely erases any existing sender's public key
+ * in the context before setting the new value. The function checks that the provided sender's public key is valid and
+ * that the context is in a mode that supports authentication and that the role of the context is Receiver.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_context_set_sender_pubkey(gnutls_hpke_context_t ctx,
+                                         gnutls_pubkey_t sender_pubkey)
+{
+       if (ctx == NULL || sender_pubkey == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (!_gnutls_hpke_is_auth_mode(ctx->mode)) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_RECEIVER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       gnutls_pubkey_deinit(ctx->sender_pubkey);
+       ctx->sender_pubkey = NULL;
+
+       return _gnutls_hpke_pubkey_clone(sender_pubkey, &ctx->sender_pubkey);
+}
+
+/**
+ * gnutls_hpke_context_get_enc_size:
+ * @ctx: The HPKE context to get the encapsulated key size for.
+ *
+ * This function returns the size of the encapsulated key (enc) that will be generated by gnutls_hpke_encap() for the
+ * given HPKE context. The size of the encapsulated key depends on the KEM algorithm used in the context. For example,
+ * for DHKEM(X25519), the encapsulated key size will be 32 bytes.
+ *
+ * It returns the size of the encapsulated key in bytes, or 0 if the context is NULL or if there is an error determining
+ * the size.
+ */
+size_t gnutls_hpke_context_get_enc_size(const gnutls_hpke_context_t ctx)
+{
+       if (ctx == NULL) {
+               return 0;
+       }
+
+       gnutls_ecc_curve_t curve = _gnutls_hpke_kem_to_curve(ctx->kem);
+       if (curve == GNUTLS_ECC_CURVE_INVALID) {
+               return 0;
+       }
+
+       return gnutls_ecc_curve_get_size(curve);
+}
+
+/**
+ * gnutls_hpke_encap:
+ * @ctx: The HPKE context to use for encapsulation.
+ * @info: A pointer to a gnutls_datum_t structure containing the application-specific information to be included in the
+ * key schedule. This parameter is optional and can be NULL if no additional information is needed.
+ * @enc: A pointer to a gnutls_datum_t structure where the encapsulated key will be stored. The function will allocate
+ * memory for the encapsulated key, and the caller is responsible for freeing this memory using gnutls_free() when it is
+ * no longer needed.
+ * @receiver_pubkey: The receiver's public key to use for encapsulation. This must be a valid public key that is
+ * compatible with the KEM algorithm specified in the HPKE context.
+ *
+ * This function performs the encapsulation operation of HPKE. It generates an encapsulated key (enc) that can be sent
+ * to the receiver, who can then use it to derive the shared secret. The function checks that the context is properly
+ * initialized and that the provided parameters are valid. It also checks that the context is in the correct role
+ * (Sender) for encapsulation.
+ *
+ * This function must be used once per HPKE context and before any calls to gnutls_hpke_seal().
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_encap(gnutls_hpke_context_t ctx, const gnutls_datum_t *info,
+                     gnutls_datum_t *enc, gnutls_pubkey_t receiver_pubkey)
+{
+       int ret;
+       if (ctx == NULL || enc == NULL || receiver_pubkey == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_SENDER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (info != NULL && info->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (_gnutls_hpke_is_auth_mode(ctx->mode)) {
+               if (ctx->sender_privkey == NULL) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+
+               ret = _gnutls_hpke_validate_privkey_for_kem(ctx->sender_privkey,
+                                                           ctx->kem);
+               if (ret < 0) {
+                       return gnutls_assert_val(ret);
+               }
+       }
+
+       if (_gnutls_hpke_is_psk_mode(ctx->mode)) {
+               if (ctx->psk == NULL || ctx->psk_id == NULL) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+       }
+
+       if (ctx->key.data != NULL || ctx->base_nonce.data != NULL ||
+           ctx->exporter_secret.data != NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = _gnutls_hpke_validate_pubkey_for_kem(receiver_pubkey, ctx->kem);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       unsigned char shared_secret[GNUTLS_HPKE_MAX_SHARED_SECRET_SIZE];
+       size_t shared_secret_size = 0;
+       if (_gnutls_is_kem_dh(ctx->kem)) {
+               ret = _gnutls_hpke_dhkem_encap(ctx, receiver_pubkey, enc,
+                                              shared_secret,
+                                              &shared_secret_size);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto error;
+               }
+       } // TODO: else if(is_mlkem(ctx->kem)) {}
+       else {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = _gnutls_hpke_schedule(shared_secret, shared_secret_size, info,
+                                   ctx);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto error;
+       }
+
+       goto cleanup;
+error:
+       if (enc->data != NULL) {
+               gnutls_free(enc->data);
+               enc->data = NULL;
+               enc->size = 0;
+       }
+cleanup:
+       if (shared_secret_size > 0) {
+               gnutls_memset(shared_secret, 0, shared_secret_size);
+       }
+
+       return ret;
+}
+
+static void _gnutls_hpke_get_seq_nonce(const gnutls_datum_t *base_nonce,
+                                      uint64_t seq, unsigned char *nonce,
+                                      size_t *nonce_size)
+{
+       memcpy(nonce, base_nonce->data, base_nonce->size);
+       *nonce_size = base_nonce->size;
+
+       for (size_t i = 0; i < 8; i++) {
+               nonce[*nonce_size - 1 - i] ^=
+                       (uint8_t)((seq >> (8 * i)) & 0xff);
+       }
+}
+
+/**
+ * gnutls_hpke_seal:
+ * @ctx: The HPKE context to use for sealing.
+ * @aad: A pointer to a gnutls_datum_t structure containing the associated data (AAD) to be authenticated but not
+ * encrypted.
+ * @plaintext: A pointer to a gnutls_datum_t structure containing the plaintext data to be encrypted and authenticated.
+ * @ciphertext: A pointer to a gnutls_datum_t structure where the resulting ciphertext will be stored. The function will
+ * allocate memory for the ciphertext, and the caller is responsible for freeing this memory using gnutls_free() when it
+ * is no longer needed.
+ *
+ * This function performs the sealing operation of HPKE. It encrypts the plaintext and computes an authentication tag
+ * using the AEAD algorithm specified in the HPKE context.
+ * The resulting ciphertext includes both the encrypted plaintext and the authentication tag.
+ *
+ * This function can be used multiple times with the same HPKE context, but the encapsulation function
+ * (gnutls_hpke_encap) must be called once before the first call to this function to set up the necessary keys and
+ * nonces in the context. Each call to this function will increment the sequence number in the context, which is used to
+ * derive unique nonces for each encryption operation.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_seal(gnutls_hpke_context_t ctx, const gnutls_datum_t *aad,
+                    const gnutls_datum_t *plaintext,
+                    gnutls_datum_t *ciphertext)
+{
+       if (ctx == NULL || aad == NULL || plaintext == NULL ||
+           ciphertext == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_SENDER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->seq == UINT64_MAX) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->aead == GNUTLS_HPKE_AEAD_EXPORT_ONLY) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->key.data == NULL || ctx->base_nonce.data == NULL ||
+           ctx->exporter_secret.data == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       const gnutls_cipher_algorithm_t cipher =
+               _gnutls_hpke_aead_to_cipher(ctx->aead);
+       if (cipher == GNUTLS_CIPHER_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_TYPE);
+       }
+
+       const uint8_t Nn = gnutls_cipher_get_iv_size(cipher);
+       if (ctx->base_nonce.size != Nn) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       int ret;
+       gnutls_aead_cipher_hd_t hd = NULL;
+
+       unsigned char nonce[GNUTLS_HPKE_MAX_NONCE_SIZE] = { 0 };
+       size_t nonce_size = 0;
+       _gnutls_hpke_get_seq_nonce(&ctx->base_nonce, ctx->seq, nonce,
+                                  &nonce_size);
+
+       ret = gnutls_aead_cipher_init(&hd, cipher, &ctx->key);
+       if (ret != 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       size_t tag_size = gnutls_cipher_get_tag_size(cipher);
+
+       ciphertext->size = plaintext->size + tag_size;
+       ciphertext->data = gnutls_malloc(ciphertext->size);
+       if (!ciphertext->data) {
+               ciphertext->size = 0;
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto cleanup;
+       }
+
+       size_t ciphertext_size = ciphertext->size;
+       ret = gnutls_aead_cipher_encrypt(hd, nonce, nonce_size, aad->data,
+                                        aad->size, tag_size, plaintext->data,
+                                        plaintext->size, ciphertext->data,
+                                        &ciphertext_size);
+       if (ret != 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+       ciphertext->size = ciphertext_size;
+
+       ctx->seq++;
+
+cleanup:
+       gnutls_memset(nonce, 0, nonce_size);
+
+       if (ret < 0 && ciphertext->data != NULL) {
+               gnutls_free(ciphertext->data);
+               ciphertext->data = NULL;
+               ciphertext->size = 0;
+       }
+
+       if (hd != NULL) {
+               gnutls_aead_cipher_deinit(hd);
+       }
+
+       return ret;
+}
+
+/**
+ * gnutls_hpke_decap:
+ * @ctx: The HPKE context to use for decapsulation.
+ * @info: A pointer to a gnutls_datum_t structure containing the application-specific information that was included in the
+ * key schedule during encapsulation. This parameter is optional and can be NULL if no additional information was used.
+ * @enc: A pointer to a gnutls_datum_t structure containing the encapsulated key received from the sender. This should be
+ * the same encapsulated key that was generated by gnutls_hpke_encap() on the sender's side.
+ * @receiver_privkey: The receiver's private key to use for decapsulation. This must be a valid private key that is
+ * compatible with the KEM algorithm specified in the HPKE context and that corresponds to the receiver's public key used
+ * during encapsulation.
+ *
+ * This function performs the decapsulation operation of HPKE. It takes the encapsulated key (enc) received from the
+ * sender and uses it along with the receiver's private key to derive the shared secret. It then uses this shared secret
+ * along with any provided application-specific information (info) to set up the necessary keys and nonces in the HPKE
+ * context for subsequent sealing and opening operations.
+ *
+ * This function must be used once per HPKE context and before any calls to gnutls_hpke_open().
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_decap(gnutls_hpke_context_t ctx, const gnutls_datum_t *info,
+                     const gnutls_datum_t *enc,
+                     gnutls_privkey_t receiver_privkey)
+{
+       int ret;
+       if (ctx == NULL || enc == NULL || receiver_privkey == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_RECEIVER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (info != NULL && info->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (_gnutls_hpke_is_auth_mode(ctx->mode)) {
+               if (ctx->sender_pubkey == NULL) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+
+               ret = _gnutls_hpke_validate_pubkey_for_kem(ctx->sender_pubkey,
+                                                          ctx->kem);
+               if (ret < 0) {
+                       return gnutls_assert_val(ret);
+               }
+       }
+
+       if (_gnutls_hpke_is_psk_mode(ctx->mode)) {
+               if (ctx->psk == NULL || ctx->psk_id == NULL) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+       }
+
+       if (ctx->key.data != NULL || ctx->base_nonce.data != NULL ||
+           ctx->exporter_secret.data != NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = _gnutls_hpke_validate_privkey_for_kem(receiver_privkey, ctx->kem);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       unsigned char shared_secret[GNUTLS_HPKE_MAX_SHARED_SECRET_SIZE];
+       size_t shared_secret_size = 0;
+       if (_gnutls_is_kem_dh(ctx->kem)) {
+               ret = _gnutls_hpke_dhkem_decap(ctx->kem, ctx->kdf, ctx->mode,
+                                              receiver_privkey,
+                                              ctx->sender_pubkey, enc,
+                                              shared_secret,
+                                              &shared_secret_size);
+               if (ret < 0) {
+                       gnutls_assert_val(ret);
+                       goto cleanup;
+               }
+       }
+       // TODO: else if(is_mlkem(ctx->kem)) {}
+       else {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = _gnutls_hpke_schedule(shared_secret, shared_secret_size, info,
+                                   ctx);
+       if (ret < 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+cleanup:
+       if (shared_secret_size > 0) {
+               gnutls_memset(shared_secret, 0, shared_secret_size);
+       }
+
+       return ret;
+}
+
+/**
+ * gnutls_hpke_open:
+ * @ctx: The HPKE context to use for opening.
+ * @aad: A pointer to a gnutls_datum_t structure containing the associated data (AAD) that was authenticated during
+ * sealing. This should be the same AAD that was provided to gnutls_hpke_seal() on the sender's side.
+ * @ciphertext: A pointer to a gnutls_datum_t structure containing the ciphertext received from the sender. This should
+ * be the same ciphertext that was generated by gnutls_hpke_seal() on the sender's side.
+ * @plaintext: A pointer to a gnutls_datum_t structure where the resulting plaintext will be stored. The function will
+ * allocate memory for the plaintext, and the caller is responsible for freeing this memory using gnutls_free() when it
+ * is no longer needed.
+ *
+ * This function performs the opening operation of HPKE. It takes the ciphertext received from the sender and uses the
+ * keys and nonces set up in the HPKE context (after decapsulation) to decrypt the ciphertext and verify the
+ * authentication tag. If the decryption and authentication are successful, the resulting plaintext is stored in the
+ * provided gnutls_datum_t structure. If the decryption or authentication fails, the function securely erases any
+ * allocated plaintext and returns an error code.
+ *
+ * This function can be used multiple times with the same HPKE context, but the decapsulation function
+ * (gnutls_hpke_decap) must be called once before the first call to this function.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_open(gnutls_hpke_context_t ctx, const gnutls_datum_t *aad,
+                    const gnutls_datum_t *ciphertext,
+                    gnutls_datum_t *plaintext)
+{
+       if (ctx == NULL || aad == NULL || ciphertext == NULL ||
+           plaintext == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_RECEIVER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->seq == UINT64_MAX) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->aead == GNUTLS_HPKE_AEAD_EXPORT_ONLY) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->key.data == NULL || ctx->base_nonce.data == NULL ||
+           ctx->exporter_secret.data == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       const gnutls_cipher_algorithm_t cipher =
+               _gnutls_hpke_aead_to_cipher(ctx->aead);
+       if (cipher == GNUTLS_CIPHER_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_UNKNOWN_CIPHER_TYPE);
+       }
+
+       const uint8_t Nn = gnutls_cipher_get_iv_size(cipher);
+       if (ctx->base_nonce.size != Nn) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       int ret;
+       gnutls_aead_cipher_hd_t hd = NULL;
+
+       unsigned char nonce[GNUTLS_HPKE_MAX_NONCE_SIZE] = { 0 };
+       size_t nonce_size = 0;
+       _gnutls_hpke_get_seq_nonce(&ctx->base_nonce, ctx->seq, nonce,
+                                  &nonce_size);
+
+       ret = gnutls_aead_cipher_init(&hd, cipher, &ctx->key);
+       if (ret != 0) {
+               gnutls_assert_val(ret);
+               goto cleanup;
+       }
+
+       size_t tag_size = gnutls_cipher_get_tag_size(cipher);
+       if (ciphertext->size < tag_size) {
+               ret = gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+               goto cleanup;
+       }
+
+       size_t plaintext_size = ciphertext->size - tag_size;
+       plaintext->size = plaintext_size;
+       plaintext->data = gnutls_malloc(plaintext->size);
+       if (!plaintext->data) {
+               plaintext->size = 0;
+               ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+               goto cleanup;
+       }
+
+       ret = gnutls_aead_cipher_decrypt(hd, nonce, nonce_size, aad->data,
+                                        aad->size, tag_size, ciphertext->data,
+                                        ciphertext->size, plaintext->data,
+                                        &plaintext_size);
+       if (ret != 0) {
+               gnutls_memset(plaintext->data, 0, plaintext->size);
+               gnutls_free(plaintext->data);
+               plaintext->data = NULL;
+               plaintext->size = 0;
+               goto cleanup;
+       }
+
+       ctx->seq++;
+cleanup:
+       gnutls_memset(nonce, 0, nonce_size);
+
+       if (hd != NULL) {
+               gnutls_aead_cipher_deinit(hd);
+       }
+
+       return ret;
+}
+
+/**
+ * gnutls_hpke_context_set_ikme:
+ * @ctx: The HPKE context to set the IKME for.
+ * @ikme: A pointer to a gnutls_datum_t structure containing the IKME value and its size.
+ *
+ * This function sets the IKME in the HPKE context. It securely erases any existing IKME in the context before setting
+ * the new value. The function checks that the provided IKME is valid and that the context is in a mode that supports
+ * IKME and that the role of the context is Sender.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_context_set_ikme(gnutls_hpke_context_t ctx,
+                                const gnutls_datum_t *ikme)
+{
+       if (ctx == NULL || ikme == NULL || ikme->data == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role != GNUTLS_HPKE_ROLE_SENDER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ikme->size == 0 || ikme->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->ikme != NULL) {
+               if (ctx->ikme->data != NULL) {
+                       gnutls_memset(ctx->ikme->data, 0, ctx->ikme->size);
+                       gnutls_free(ctx->ikme->data);
+                       ctx->ikme->data = NULL;
+                       ctx->ikme->size = 0;
+               }
+               gnutls_free(ctx->ikme);
+               ctx->ikme = NULL;
+       }
+
+       ctx->ikme = gnutls_malloc(sizeof(*ctx->ikme));
+       if (ctx->ikme == NULL) {
+               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+       }
+
+       ctx->ikme->size = ikme->size;
+       ctx->ikme->data = gnutls_malloc(ctx->ikme->size);
+       if (ctx->ikme->data == NULL) {
+               gnutls_free(ctx->ikme);
+               ctx->ikme = NULL;
+               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+       }
+
+       memcpy(ctx->ikme->data, ikme->data, ikme->size);
+
+       return 0;
+}
+
+/**
+ * gnutls_hpke_generate_keypair:
+ * @kem: The KEM algorithm to use for key pair generation.
+ * @ikm: A pointer to a gnutls_datum_t structure containing the input key material (IKM) to be used for key pair
+ * generation. This should be a non-empty byte string that serves as the seed for key pair generation.
+ * @privkey: A pointer to a gnutls_privkey_t variable where the generated private key will be stored. The function will initialize this variable with the generated private key.
+ * @pubkey: A pointer to a gnutls_pubkey_t variable where the generated public key will be stored. The function will initialize this variable with the generated public key.
+ *
+ * This function generates a key pair (private key and public key) for the specified KEM algorithm using the provided
+ * input key material (IKM). The IKM is used as a seed for the key generation process, allowing for deterministic key
+ * pair generation if the same IKM is used. The function checks that the provided parameters are valid and that the KEM
+ * algorithm is supported.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_generate_keypair(const gnutls_hpke_kem_t kem,
+                                const gnutls_datum_t *ikm,
+                                gnutls_privkey_t *privkey,
+                                gnutls_pubkey_t *pubkey)
+{
+       int ret;
+       if (ikm == NULL || ikm->data == NULL || ikm->size == 0 ||
+           privkey == NULL || pubkey == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = _gnutls_hpke_keypair_from_ikm(kem, ikm, privkey, pubkey);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       return 0;
+}
+
+/**
+ * gnutls_hpke_get_seq:
+ * @ctx: The HPKE context to get the sequence number from.
+ * @seq: A pointer to a uint64_t variable where the current sequence number will be stored.
+ *
+ * This function retrieves the current sequence number from the HPKE context. The sequence number is used to derive
+ * unique nonces for encryption and decryption operations in HPKE. The function checks that the provided parameters are
+ * valid and that the context is properly initialized.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_get_seq(gnutls_hpke_context_t ctx, uint64_t *seq)
+{
+       if (ctx == NULL || seq == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       *seq = ctx->seq;
+       return 0;
+}
+
+/**
+ * gnutls_hpke_set_seq:
+ * @ctx: The HPKE context to set the sequence number for.
+ * @seq: The sequence number to set in the context.
+ *
+ * This function sets the sequence number in the HPKE context. The sequence number is used to derive unique nonces for
+ * encryption and decryption operations in HPKE. The function checks that the provided parameters are valid and that the
+ * context is properly initialized and that the role of the context is Receiver, as only the receiver should be setting
+ * the sequence number (the sender's sequence number is managed internally by gnutls_hpke_seal()).
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_set_seq(gnutls_hpke_context_t ctx, uint64_t seq)
+{
+       if (ctx == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->role == GNUTLS_HPKE_ROLE_SENDER) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ctx->seq = seq;
+       return 0;
+}
+
+/**
+ * gnutls_hpke_export:
+ * @ctx: The HPKE context to use for exporting the secret.
+ * @exporter_context: A pointer to a gnutls_datum_t structure containing the application-specific context to be included
+ * in the export. 
+ * @L: The length in bytes of the secret to be exported. This should be a positive integer that does not exceed the
+ * maximum allowed size for HPKE exports.
+ * @secret: A pointer to a gnutls_datum_t structure where the exported secret will be stored. The function will allocate
+ * memory for the secret, and the caller is responsible for freeing this memory using gnutls_free() when it is no longer
+ * needed.
+ *
+ * This function performs the export operation of HPKE. It derives a secret of length L bytes from the exporter secret in
+ * the HPKE context, using the provided application-specific context and the KDF specified in the context. The
+ * resulting secret is stored in the provided gnutls_datum_t structure. The function checks that the provided parameters
+ * are valid and that the context is properly initialized and that there is an exporter secret available in the context.
+ *
+ * It returns 0 on success, or a negative error code on failure.
+ */
+int gnutls_hpke_export(gnutls_hpke_context_t ctx,
+                      const gnutls_datum_t *exporter_context, const size_t L,
+                      gnutls_datum_t *secret)
+
+{
+       if (ctx == NULL || exporter_context == NULL || secret == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (ctx->exporter_secret.data == NULL) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       if (exporter_context->size > GNUTLS_HPKE_MAX_PARAMETER_SIZE) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       int ret;
+       unsigned char suite_id[GNUTLS_SCHEDULING_SUITE_ID_SIZE];
+
+       _gnutls_hpke_build_suite_id_for_scheduling(ctx->kem, ctx->kdf,
+                                                  ctx->aead, suite_id);
+
+       unsigned char
+               labeled_export_info[GNUTLS_HPKE_MAX_LABELED_EXPORT_INFO_MAX_SIZE];
+       size_t labeled_export_info_size = 0;
+
+       _gnutls_hpke_build_expand_info(
+               suite_id, GNUTLS_SCHEDULING_SUITE_ID_SIZE, export_secret_label,
+               sizeof(export_secret_label) - 1, exporter_context->data,
+               exporter_context->size, L, labeled_export_info,
+               &labeled_export_info_size);
+
+       const gnutls_mac_algorithm_t mac = _gnutls_hpke_kdf_to_mac(ctx->kdf);
+       if (mac == GNUTLS_MAC_UNKNOWN) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       gnutls_datum_t expand_info = { labeled_export_info,
+                                      labeled_export_info_size };
+       secret->data = gnutls_malloc(L);
+       if (secret->data == NULL) {
+               return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+       }
+
+       ret = gnutls_hkdf_expand(mac, &ctx->exporter_secret, &expand_info,
+                                secret->data, L);
+       if (ret < 0) {
+               gnutls_memset(secret->data, 0, L);
+               gnutls_free(secret->data);
+               secret->data = NULL;
+               secret->size = 0;
+               return gnutls_assert_val(ret);
+       }
+
+       secret->size = L;
+
+       return 0;
+}
index bde928af97a09aef1ffee0f2acb3ec6f4c5fc017..7549ab0a6c5725031342e9cfb44674424b016c45 100644 (file)
@@ -30,6 +30,8 @@
 #include <gnutls/openpgp.h>
 #include <gnutls/tpm.h>
 
+#include <stdint.h>
+
 #ifdef __cplusplus
 extern "C" {
 #endif
@@ -719,70 +721,74 @@ typedef enum gnutls_hpke_kdf_t {
 typedef enum gnutls_hpke_aead_t {
        GNUTLS_HPKE_AEAD_AES_128_GCM = 0x0001,
        GNUTLS_HPKE_AEAD_AES_256_GCM = 0x0002,
-       GNUTLS_HPKE_AEAD_CHACHA20_POLY1305 = 0x0003
+       GNUTLS_HPKE_AEAD_CHACHA20_POLY1305 = 0x0003,
+       GNUTLS_HPKE_AEAD_EXPORT_ONLY = 0xFFFF
 } gnutls_hpke_aead_t;
 
-/**
- * gnutls_hpke_encap_context_t:
- * @kem: KEM algorithm
- * @kdf: KDF algorithm
- * @aead: AEAD algorithm
- * @info: application specific information (optional)
- * @psk: pre-shared key (optional)
- * @psk_id: pre-shared key identifier (optional)
- * @receiver_pubkey: receiver's public key
- * @sender_privkey: sender's private key (optional)
- * Context for HPKE encapsulation.
- */
-typedef struct gnutls_hpke_encap_context_t {
-       const gnutls_hpke_kem_t kem;
-       const gnutls_hpke_kdf_t kdf;
-       const gnutls_hpke_aead_t aead;
+typedef enum gnutls_hpke_mode_t {
+       GNUTLS_HPKE_MODE_BASE = 0,
+       GNUTLS_HPKE_MODE_PSK = 1,
+       GNUTLS_HPKE_MODE_AUTH = 2,
+       GNUTLS_HPKE_MODE_AUTH_PSK = 3
+} gnutls_hpke_mode_t;
 
-       const gnutls_datum_t *info;
-       const gnutls_datum_t *psk;
-       const gnutls_datum_t *psk_id;
+typedef enum gnutls_hpke_role_t {
+       GNUTLS_HPKE_ROLE_SENDER = 0,
+       GNUTLS_HPKE_ROLE_RECEIVER = 1
+} gnutls_hpke_role_t;
 
-       const gnutls_pubkey_t receiver_pubkey;
-       const gnutls_privkey_t sender_privkey;
-} gnutls_hpke_encap_context_t;
+typedef struct gnutls_hpke_context_st *gnutls_hpke_context_t;
 
-/**
- * gnutls_hpke_decap_context_t:
- * @kem: KEM algorithm
- * @kdf: KDF algorithm
- * @aead: AEAD algorithm
- * @info: application specific information (optional)
- * @psk: pre-shared key (optional)
- * @psk_id: pre-shared key identifier (optional)
- * @enc: encapsulated key
- * @receiver_privkey: receiver's private key
- * @sender_pubkey: sender's public key (optional)
- * Context for HPKE decapsulation.
- */
-typedef struct gnutls_hpke_decap_context_t {
-       const gnutls_hpke_kem_t kem;
-       const gnutls_hpke_kdf_t kdf;
-       const gnutls_hpke_aead_t aead;
+int gnutls_hpke_context_init(gnutls_hpke_context_t *ctx,
+                            const gnutls_hpke_mode_t mode,
+                            const gnutls_hpke_role_t role,
+                            const gnutls_hpke_kem_t kem,
+                            const gnutls_hpke_kdf_t kdf,
+                            const gnutls_hpke_aead_t aead);
+
+int gnutls_hpke_context_deinit(gnutls_hpke_context_t ctx);
+
+int gnutls_hpke_context_set_psk(gnutls_hpke_context_t ctx,
+                               const gnutls_datum_t *psk,
+                               const gnutls_datum_t *psk_id);
+
+int gnutls_hpke_context_set_sender_privkey(gnutls_hpke_context_t ctx,
+                                          gnutls_privkey_t sender_privkey);
+
+int gnutls_hpke_context_set_sender_pubkey(gnutls_hpke_context_t ctx,
+                                         gnutls_pubkey_t sender_pubkey);
+
+size_t gnutls_hpke_context_get_enc_size(const gnutls_hpke_context_t ctx);
+
+int gnutls_hpke_encap(gnutls_hpke_context_t ctx, const gnutls_datum_t *info,
+                     gnutls_datum_t *enc, gnutls_pubkey_t receiver_pubkey);
+
+int gnutls_hpke_seal(gnutls_hpke_context_t ctx, const gnutls_datum_t *aad,
+                    const gnutls_datum_t *plaintext,
+                    gnutls_datum_t *ciphertext);
+
+int gnutls_hpke_decap(gnutls_hpke_context_t ctx, const gnutls_datum_t *info,
+                     const gnutls_datum_t *enc,
+                     gnutls_privkey_t receiver_privkey);
 
-       const gnutls_datum_t *info;
-       const gnutls_datum_t *psk;
-       const gnutls_datum_t *psk_id;
+int gnutls_hpke_open(gnutls_hpke_context_t ctx, const gnutls_datum_t *aad,
+                    const gnutls_datum_t *ciphertext,
+                    gnutls_datum_t *plaintext);
 
-       const gnutls_datum_t *enc;
-       const gnutls_privkey_t receiver_privkey;
-       const gnutls_pubkey_t sender_pubkey;
-} gnutls_hpke_decap_context_t;
+int gnutls_hpke_context_set_ikme(gnutls_hpke_context_t ctx,
+                                const gnutls_datum_t *ikme);
 
-int gnutls_hpke_encap(const gnutls_hpke_encap_context_t *ctx,
-                     gnutls_datum_t *enc, gnutls_datum_t *key,
-                     gnutls_datum_t *base_nonce,
-                     gnutls_datum_t *exporter_secret);
+int gnutls_hpke_generate_keypair(const gnutls_hpke_kem_t kem,
+                                const gnutls_datum_t *ikm,
+                                gnutls_privkey_t *privkey,
+                                gnutls_pubkey_t *pubkey);
 
-int gnutls_hpke_decap(const gnutls_hpke_decap_context_t *ctx,
-                     gnutls_datum_t *key, gnutls_datum_t *base_nonce,
-                     gnutls_datum_t *exporter_secret);
+int gnutls_hpke_get_seq(gnutls_hpke_context_t ctx, uint64_t *seq);
+int gnutls_hpke_set_seq(gnutls_hpke_context_t ctx, uint64_t seq);
 
+int gnutls_hpke_export(gnutls_hpke_context_t ctx,
+                      const gnutls_datum_t *exporter_context, const size_t L,
+                      gnutls_datum_t *secret);
 #ifdef __cplusplus
 }
 #endif
index 96b2e0130b161e18fd31f17a4a67045b8bc9ca41..2fe8ad657902e0dd3f2cd20303931e8a354f5e92 100644 (file)
@@ -1470,6 +1470,19 @@ GNUTLS_3_8_12
     gnutls_pkcs11_obj_get_pk_algorithm;
     gnutls_hpke_encap;
     gnutls_hpke_decap;
+    gnutls_hpke_context_init;
+    gnutls_hpke_context_deinit;
+    gnutls_hpke_context_set_psk;
+    gnutls_hpke_context_set_sender_privkey;
+    gnutls_hpke_context_set_sender_pubkey;
+    gnutls_hpke_context_get_enc_size;
+    gnutls_hpke_seal;
+    gnutls_hpke_open;
+    gnutls_hpke_context_set_ikme;
+    gnutls_hpke_generate_keypair;
+    gnutls_hpke_get_seq;
+    gnutls_hpke_set_seq;
+    gnutls_hpke_export;
  local:
        *;
 } GNUTLS_3_8_11;
index 76c4a08926a1ee958d60cece403bf16bfc878a89..77f1d36d38403fbbabd5816589f977045389d919 100644 (file)
@@ -1341,20 +1341,16 @@ int gnutls_x509_privkey_import_ecc_raw(gnutls_x509_privkey_t key,
                        goto cleanup;
                }
 
-               if (curve_is_eddsa(curve)) {
-                       size = gnutls_ecc_curve_get_size(curve);
-                       if (x->size != size || k->size != size) {
-                               ret = gnutls_assert_val(
-                                       GNUTLS_E_INVALID_REQUEST);
-                               goto cleanup;
-                       }
+               size = gnutls_ecc_curve_get_size(curve);
+               if (x->size != size || k->size != size) {
+                       ret = gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+                       goto cleanup;
+               }
 
-                       ret = _gnutls_set_datum(&key->params.raw_pub, x->data,
-                                               x->size);
-                       if (ret < 0) {
-                               gnutls_assert();
-                               goto cleanup;
-                       }
+               ret = _gnutls_set_datum(&key->params.raw_pub, x->data, x->size);
+               if (ret < 0) {
+                       gnutls_assert();
+                       goto cleanup;
                }
 
                ret = _gnutls_set_datum(&key->params.raw_priv, k->data,
index 15d737c17b7ab1a0371cffab3b251b52bf348413..6a641d571963c8eaded9f918a83bf3590b9e376e 100644 (file)
@@ -241,7 +241,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
         x509cert-dntypes id-on-xmppAddr tls13-compat-mode ciphersuite-name \
         x509-upnconstraint xts-key-check cipher-padding pkcs7-verify-double-free \
         fips-rsa-sizes tls12-rehandshake-ticket pathbuf tls-force-ems \
-        psk-importer privkey-derive dh-compute2 ecdh-compute2 hpke
+        psk-importer privkey-derive dh-compute2 ecdh-compute2 hpke-tests
 
 ctests += tls-channel-binding
 
diff --git a/tests/hpke-tests.c b/tests/hpke-tests.c
new file mode 100644 (file)
index 0000000..9aee68d
--- /dev/null
@@ -0,0 +1,3472 @@
+/*
+ * Copyright © 2025 David Dudas
+ *
+ * Author: David Dudas <david.dudas03@e-uvt.ro>
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
+
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <gnutls/gnutls.h>
+#include <gnutls/crypto.h>
+#include <gnutls/abstract.h>
+
+#include "utils.h"
+
+#include <stdbool.h>
+#include <stdint.h>
+
+static unsigned char info[] = { 0x4f, 0x64, 0x65, 0x20, 0x6f, 0x6e, 0x20,
+                               0x61, 0x20, 0x47, 0x72, 0x65, 0x63, 0x69,
+                               0x61, 0x6e, 0x20, 0x55, 0x72, 0x6e };
+
+static unsigned char plaintext[] = { 0x42, 0x65, 0x61, 0x75, 0x74, 0x79,
+                                    0x20, 0x69, 0x73, 0x20, 0x74, 0x72,
+                                    0x75, 0x74, 0x68, 0x2c, 0x20, 0x74,
+                                    0x72, 0x75, 0x74, 0x68, 0x20, 0x62,
+                                    0x65, 0x61, 0x75, 0x74, 0x79 };
+
+static unsigned char seq0_aad[] = { 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x30 };
+
+static unsigned char seq1_aad[] = { 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x31 };
+
+static unsigned char seq2_aad[] = { 0x43, 0x6f, 0x75, 0x6e, 0x74, 0x2d, 0x32 };
+
+static unsigned char exporter_context_1[] = {};
+
+static unsigned char exporter_context_2[] = { 0x00 };
+
+static unsigned char exporter_context_3[] = { 0x54, 0x65, 0x73, 0x74,
+                                             0x43, 0x6f, 0x6e, 0x74,
+                                             0x65, 0x78, 0x74 };
+
+static unsigned char psk[] = { 0x02, 0x47, 0xfd, 0x33, 0xb9, 0x13, 0x76, 0x0f,
+                              0xa1, 0xfa, 0x51, 0xe1, 0x89, 0x2d, 0x9f, 0x30,
+                              0x7f, 0xbe, 0x65, 0xeb, 0x17, 0x1e, 0x81, 0x32,
+                              0xc2, 0xaf, 0x18, 0x55, 0x5a, 0x73, 0x8b, 0x82 };
+
+static unsigned char psk_id[] = { 0x45, 0x6e, 0x6e, 0x79, 0x6e, 0x20,
+                                 0x44, 0x75, 0x72, 0x69, 0x6e, 0x20,
+                                 0x61, 0x72, 0x61, 0x6e, 0x20, 0x4d,
+                                 0x6f, 0x72, 0x69, 0x61 };
+
+typedef struct hpke_test_encryption_parameters_st {
+       size_t sequence_number;
+       gnutls_datum_t plaintext;
+       gnutls_datum_t aad;
+       gnutls_datum_t expected_ciphertext;
+} hpke_test_encryption_parameters_st;
+
+typedef struct hpke_test_exporter_parameters_st {
+       gnutls_datum_t exporter_context;
+       size_t exporter_length;
+       gnutls_datum_t expected_exporter_value;
+} hpke_test_exporter_parameters_st;
+
+typedef struct hpke_test_parameters_st {
+       gnutls_hpke_mode_t mode;
+       gnutls_hpke_kem_t kem;
+       gnutls_hpke_kdf_t kdf;
+       gnutls_hpke_aead_t aead;
+       gnutls_datum_t ikmE;
+       gnutls_datum_t ikmR;
+       gnutls_datum_t *ikmS;
+       gnutls_datum_t info;
+       gnutls_datum_t *psk;
+       gnutls_datum_t *psk_id;
+       gnutls_datum_t expected_enc;
+       hpke_test_encryption_parameters_st *encryption_parameters;
+       size_t num_encryption_parameters;
+       hpke_test_exporter_parameters_st *exporter_parameters;
+       size_t num_exporter_parameters;
+} hpke_test_parameters_st;
+
+static void test_hpke(const hpke_test_parameters_st *params)
+{
+       int ret;
+       gnutls_privkey_t skR = NULL;
+       gnutls_pubkey_t pkR = NULL;
+       gnutls_privkey_t skS = NULL;
+       gnutls_pubkey_t pkS = NULL;
+
+       gnutls_hpke_context_t sender_ctx = NULL;
+       gnutls_hpke_context_t receiver_ctx = NULL;
+
+       gnutls_datum_t enc = { NULL, 0 };
+       gnutls_datum_t plaintext_out = { NULL, 0 };
+       gnutls_datum_t ciphertext_out = { NULL, 0 };
+       gnutls_datum_t exporter_out = { NULL, 0 };
+
+       ret = gnutls_hpke_context_init(&sender_ctx, params->mode,
+                                      GNUTLS_HPKE_ROLE_SENDER, params->kem,
+                                      params->kdf, params->aead);
+       if (ret < 0) {
+               fail("gnutls_hpke_context_init (mode: %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hpke_context_set_ikme(sender_ctx, &params->ikmE);
+       if (ret < 0) {
+               fail("gnutls_hpke_context_set_ikme (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       if (params->psk != NULL && params->psk_id != NULL) {
+               ret = gnutls_hpke_context_set_psk(sender_ctx, params->psk,
+                                                 params->psk_id);
+               if (ret < 0) {
+                       fail("gnutls_hpke_context_set_psk (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+       }
+
+       if (params->ikmS != NULL) {
+               ret = gnutls_hpke_generate_keypair(params->kem, params->ikmS,
+                                                  &skS, &pkS);
+               if (ret < 0) {
+                       fail("gnutls_hpke_generate_keypair (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+
+               ret = gnutls_hpke_context_set_sender_privkey(sender_ctx, skS);
+               if (ret < 0) {
+                       fail("gnutls_hpke_context_set_sender_privkey (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+       }
+
+       ret = gnutls_hpke_generate_keypair(params->kem, &params->ikmR, &skR,
+                                          &pkR);
+       if (ret < 0) {
+               fail("gnutls_hpke_generate_keypair (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       ret = gnutls_hpke_encap(sender_ctx, &params->info, &enc, pkR);
+       if (ret < 0) {
+               fail("gnutls_hpke_encap (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       if (params->expected_enc.size != enc.size ||
+           memcmp(params->expected_enc.data, enc.data, enc.size) != 0) {
+               fail("enc does not match expected value (mode %d, kem: %d, kdf: %d, aead: %d)\n",
+                    params->mode, params->kem, params->kdf, params->aead);
+               goto cleanup;
+       }
+
+       ret = gnutls_hpke_context_init(&receiver_ctx, params->mode,
+                                      GNUTLS_HPKE_ROLE_RECEIVER, params->kem,
+                                      params->kdf, params->aead);
+       if (ret < 0) {
+               fail("gnutls_context_init (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       if (params->psk != NULL && params->psk_id != NULL) {
+               ret = gnutls_hpke_context_set_psk(receiver_ctx, params->psk,
+                                                 params->psk_id);
+               if (ret < 0) {
+                       fail("gnutls_hpke_context_set_psk (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+       }
+
+       if (params->ikmS != NULL) {
+               ret = gnutls_hpke_context_set_sender_pubkey(receiver_ctx, pkS);
+               if (ret < 0) {
+                       fail("gnutls_hpke_context_set_sender_pubkey (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+       }
+
+       ret = gnutls_hpke_decap(receiver_ctx, &params->info, &enc, skR);
+       if (ret < 0) {
+               fail("gnutls_hpke_decap (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                    params->mode, params->kem, params->kdf, params->aead,
+                    gnutls_strerror(ret));
+               goto cleanup;
+       }
+
+       for (size_t i = 0; i < params->num_encryption_parameters; i++) {
+               hpke_test_encryption_parameters_st *enc_params =
+                       &params->encryption_parameters[i];
+               ret = gnutls_hpke_seal(sender_ctx, &enc_params->aad,
+                                      &enc_params->plaintext, &ciphertext_out);
+               if (ret < 0) {
+                       fail("gnutls_hpke_seal (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+
+               if (enc_params->expected_ciphertext.size !=
+                           ciphertext_out.size ||
+                   memcmp(enc_params->expected_ciphertext.data,
+                          ciphertext_out.data, ciphertext_out.size) != 0) {
+                       fail("ciphertext does not match expected value (mode %d, kem: %d, kdf: %d, aead: %d)\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead);
+                       goto cleanup;
+               }
+
+               ret = gnutls_hpke_open(receiver_ctx, &enc_params->aad,
+                                      &ciphertext_out, &plaintext_out);
+               if (ret < 0) {
+                       fail("gnutls_hpke_open (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+
+               if (enc_params->plaintext.size != plaintext_out.size ||
+                   memcmp(enc_params->plaintext.data, plaintext_out.data,
+                          plaintext_out.size) != 0) {
+                       fail("decrypted plaintext does not match original plaintext (mode %d, kem: %d, kdf: %d, aead: %d)\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead);
+                       goto cleanup;
+               }
+
+               uint64_t seq;
+               ret = gnutls_hpke_get_seq(receiver_ctx, &seq);
+               if (ret < 0) {
+                       fail("gnutls_hpke_get_seq (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+
+               if (seq != enc_params->sequence_number + 1) {
+                       fail("sequence number does not match expected value (mode %d, kem: %d, kdf: %d, aead: %d)\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead);
+                       goto cleanup;
+               }
+
+               gnutls_free(ciphertext_out.data);
+               ciphertext_out.data = NULL;
+               gnutls_free(plaintext_out.data);
+               plaintext_out.data = NULL;
+       }
+
+       for (size_t i = 0; i < params->num_exporter_parameters; i++) {
+               hpke_test_exporter_parameters_st *exp_params =
+                       &params->exporter_parameters[i];
+               ret = gnutls_hpke_export(sender_ctx,
+                                        &exp_params->exporter_context, 32,
+                                        &exporter_out);
+               if (ret < 0) {
+                       fail("gnutls_hpke_export (mode %d, kem: %d, kdf: %d, aead: %d) failed: %s\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead, gnutls_strerror(ret));
+                       goto cleanup;
+               }
+
+               if (exp_params->expected_exporter_value.size !=
+                           exporter_out.size ||
+                   memcmp(exp_params->expected_exporter_value.data,
+                          exporter_out.data, exporter_out.size) != 0) {
+                       fail("exported value does not match expected value (mode %d, kem: %d, kdf: %d, aead: %d)\n",
+                            params->mode, params->kem, params->kdf,
+                            params->aead);
+                       goto cleanup;
+               }
+
+               gnutls_free(exporter_out.data);
+               exporter_out.data = NULL;
+       }
+
+cleanup:
+       gnutls_privkey_deinit(skR);
+       gnutls_pubkey_deinit(pkR);
+       gnutls_privkey_deinit(skS);
+       gnutls_pubkey_deinit(pkS);
+       gnutls_hpke_context_deinit(sender_ctx);
+       gnutls_hpke_context_deinit(receiver_ctx);
+
+       if (enc.data != NULL) {
+               gnutls_free(enc.data);
+       }
+
+       if (plaintext_out.data != NULL) {
+               gnutls_free(plaintext_out.data);
+       }
+
+       if (ciphertext_out.data != NULL) {
+               gnutls_free(ciphertext_out.data);
+       }
+
+       if (exporter_out.data != NULL) {
+               gnutls_free(exporter_out.data);
+       }
+}
+
+static void rfc9180_a11(void)
+{
+       unsigned char a111_seq0_expected_ct[] = {
+               0xf9, 0x38, 0x55, 0x8b, 0x5d, 0x72, 0xf1, 0xa2, 0x38,
+               0x10, 0xb4, 0xbe, 0x2a, 0xb4, 0xf8, 0x43, 0x31, 0xac,
+               0xc0, 0x2f, 0xc9, 0x7b, 0xab, 0xc5, 0x3a, 0x52, 0xae,
+               0x82, 0x18, 0xa3, 0x55, 0xa9, 0x6d, 0x87, 0x70, 0xac,
+               0x83, 0xd0, 0x7b, 0xea, 0x87, 0xe1, 0x3c, 0x51, 0x2a
+       };
+
+       unsigned char a111_seq1_expected_ct[] = {
+               0xaf, 0x2d, 0x7e, 0x9a, 0xc9, 0xae, 0x7e, 0x27, 0x0f,
+               0x46, 0xba, 0x1f, 0x97, 0x5b, 0xe5, 0x3c, 0x09, 0xf8,
+               0xd8, 0x75, 0xbd, 0xc8, 0x53, 0x54, 0x58, 0xc2, 0x49,
+               0x4e, 0x8a, 0x6e, 0xab, 0x25, 0x1c, 0x03, 0xd0, 0xc2,
+               0x2a, 0x56, 0xb8, 0xca, 0x42, 0xc2, 0x06, 0x3b, 0x84
+       };
+
+       unsigned char a111_seq2_expected_ct[] = {
+               0x49, 0x8d, 0xfc, 0xab, 0xd9, 0x2e, 0x8a, 0xce, 0xdc,
+               0x28, 0x1e, 0x85, 0xaf, 0x1c, 0xb4, 0xe3, 0xe3, 0x1c,
+               0x7d, 0xc3, 0x94, 0xa1, 0xca, 0x20, 0xe1, 0x73, 0xcb,
+               0x72, 0x51, 0x64, 0x91, 0x58, 0x8d, 0x96, 0xa1, 0x9a,
+               0xd4, 0xa6, 0x83, 0x51, 0x89, 0x73, 0xdc, 0xc1, 0x80
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { a111_seq0_expected_ct,
+                                          sizeof(a111_seq0_expected_ct) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { a111_seq1_expected_ct,
+                                          sizeof(a111_seq1_expected_ct) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { a111_seq2_expected_ct,
+                                          sizeof(a111_seq2_expected_ct) } }
+       };
+
+       unsigned char a112_expected_exporter_value_1[] = {
+               0x38, 0x53, 0xfe, 0x2b, 0x40, 0x35, 0x19, 0x5a,
+               0x57, 0x3f, 0xfc, 0x53, 0x85, 0x6e, 0x77, 0x05,
+               0x8e, 0x15, 0xd9, 0xea, 0x06, 0x4d, 0xe3, 0xe5,
+               0x9f, 0x49, 0x61, 0xd0, 0x09, 0x52, 0x50, 0xee
+       };
+
+       unsigned char a112_expected_exporter_value_2[] = {
+               0x2e, 0x8f, 0x0b, 0x54, 0x67, 0x3c, 0x70, 0x29,
+               0x64, 0x9d, 0x4e, 0xb9, 0xd5, 0xe3, 0x3b, 0xf1,
+               0x87, 0x2c, 0xf7, 0x6d, 0x62, 0x3f, 0xf1, 0x64,
+               0xac, 0x18, 0x5d, 0xa9, 0xe8, 0x8c, 0x21, 0xa5
+       };
+
+       unsigned char a112_expected_exporter_value_3[] = {
+               0xe9, 0xe4, 0x30, 0x65, 0x10, 0x2c, 0x38, 0x36,
+               0x40, 0x1b, 0xed, 0x8c, 0x3c, 0x3c, 0x75, 0xae,
+               0x46, 0xbe, 0x16, 0x39, 0x86, 0x93, 0x91, 0xd6,
+               0x2c, 0x61, 0xf1, 0xec, 0x7a, 0xf5, 0x49, 0x31
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(a112_expected_exporter_value_1),
+                 .expected_exporter_value = { a112_expected_exporter_value_1,
+                                              sizeof(a112_expected_exporter_value_1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(a112_expected_exporter_value_2),
+                 .expected_exporter_value = { a112_expected_exporter_value_2,
+                                              sizeof(a112_expected_exporter_value_2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(a112_expected_exporter_value_3),
+                 .expected_exporter_value = { a112_expected_exporter_value_3,
+                                              sizeof(a112_expected_exporter_value_3) } }
+       };
+
+       unsigned char a11_ikmE[] = { 0x72, 0x68, 0x60, 0x0d, 0x40, 0x3f, 0xce,
+                                    0x43, 0x15, 0x61, 0xae, 0xf5, 0x83, 0xee,
+                                    0x16, 0x13, 0x52, 0x7c, 0xff, 0x65, 0x5c,
+                                    0x13, 0x43, 0xf2, 0x98, 0x12, 0xe6, 0x67,
+                                    0x06, 0xdf, 0x32, 0x34 };
+
+       unsigned char a11_ikmR[] = { 0x6d, 0xb9, 0xdf, 0x30, 0xaa, 0x07, 0xdd,
+                                    0x42, 0xee, 0x5e, 0x81, 0x81, 0xaf, 0xdb,
+                                    0x97, 0x7e, 0x53, 0x8f, 0x5e, 0x1f, 0xec,
+                                    0x8a, 0x06, 0x22, 0x3f, 0x33, 0xf7, 0x01,
+                                    0x3e, 0x52, 0x50, 0x37 };
+
+       unsigned char a11_expected_enc[] = { 0x37, 0xfd, 0xa3, 0x56, 0x7b, 0xdb,
+                                            0xd6, 0x28, 0xe8, 0x86, 0x68, 0xc3,
+                                            0xc8, 0xd7, 0xe9, 0x7d, 0x1d, 0x12,
+                                            0x53, 0xb6, 0xd4, 0xea, 0x6d, 0x44,
+                                            0xc1, 0x50, 0xf7, 0x41, 0xf1, 0xbf,
+                                            0x44, 0x31 };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { a11_ikmE, sizeof(a11_ikmE) },
+               .ikmR = { a11_ikmR, sizeof(a11_ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { a11_expected_enc, sizeof(a11_expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a12(void)
+{
+       unsigned char a12_seq0_expected_ct[] = {
+               0xe5, 0x2c, 0x6f, 0xed, 0x7f, 0x75, 0x8d, 0x0c, 0xf7,
+               0x14, 0x56, 0x89, 0xf2, 0x1b, 0xc1, 0xbe, 0x6e, 0xc9,
+               0xea, 0x09, 0x7f, 0xef, 0x4e, 0x95, 0x94, 0x40, 0x01,
+               0x2f, 0x4f, 0xeb, 0x73, 0xfb, 0x61, 0x1b, 0x94, 0x61,
+               0x99, 0xe6, 0x81, 0xf4, 0xcf, 0xc3, 0x4d, 0xb8, 0xea
+       };
+       unsigned char a12_seq1_expected_ct[] = {
+               0x49, 0xf3, 0xb1, 0x9b, 0x28, 0xa9, 0xea, 0x9f, 0x43,
+               0xe8, 0xc7, 0x12, 0x04, 0xc0, 0x0d, 0x4a, 0x49, 0x0e,
+               0xe7, 0xf6, 0x13, 0x87, 0xb6, 0x71, 0x9d, 0xb7, 0x65,
+               0xe9, 0x48, 0x12, 0x3b, 0x45, 0xb6, 0x16, 0x33, 0xef,
+               0x05, 0x9b, 0xa2, 0x2c, 0xd6, 0x24, 0x37, 0xc8, 0xba
+       };
+       unsigned char a12_seq2_expected_ct[] = {
+               0x25, 0x7c, 0xa6, 0xa0, 0x84, 0x73, 0xdc, 0x85, 0x1f,
+               0xde, 0x45, 0xaf, 0xd5, 0x98, 0xcc, 0x83, 0xe3, 0x26,
+               0xdd, 0xd0, 0xab, 0xe1, 0xef, 0x23, 0xba, 0xa3, 0xba,
+               0xa4, 0xdd, 0x8c, 0xde, 0x99, 0xfc, 0xe2, 0xc1, 0xe8,
+               0xce, 0x68, 0x7b, 0x0b, 0x47, 0xea, 0xd1, 0xad, 0xc9
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { a12_seq0_expected_ct,
+                                          sizeof(a12_seq0_expected_ct) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { a12_seq1_expected_ct,
+                                          sizeof(a12_seq1_expected_ct) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { a12_seq2_expected_ct,
+                                          sizeof(a12_seq2_expected_ct) } }
+       };
+
+       unsigned char a12_expected_exporter_value_1[] = {
+               0xdf, 0xf1, 0x7a, 0xf3, 0x54, 0xc8, 0xb4, 0x16,
+               0x73, 0x56, 0x7d, 0xb6, 0x25, 0x9f, 0xd6, 0x02,
+               0x99, 0x67, 0xb4, 0xe1, 0xaa, 0xd1, 0x30, 0x23,
+               0xc2, 0xae, 0x5d, 0xf8, 0xf4, 0xf4, 0x3b, 0xf6
+       };
+
+       unsigned char a12_expected_exporter_value_2[] = {
+               0x6a, 0x84, 0x72, 0x61, 0xd8, 0x20, 0x7f, 0xe5,
+               0x96, 0xbe, 0xfb, 0x52, 0x92, 0x84, 0x63, 0x88,
+               0x1a, 0xb4, 0x93, 0xda, 0x34, 0x5b, 0x10, 0xe1,
+               0xdc, 0xc6, 0x45, 0xe3, 0xb9, 0x4e, 0x2d, 0x95
+       };
+
+       unsigned char a12_expected_exporter_value_3[] = {
+               0x8a, 0xff, 0x52, 0xb4, 0x5a, 0x1b, 0xe3, 0xa7,
+               0x34, 0xbc, 0x7a, 0x41, 0xe2, 0x0b, 0x4e, 0x05,
+               0x5a, 0xd4, 0xc4, 0xd2, 0x21, 0x04, 0xb0, 0xc2,
+               0x02, 0x85, 0xa7, 0xc4, 0x30, 0x24, 0x01, 0xcd
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(a12_expected_exporter_value_1),
+                 .expected_exporter_value = { a12_expected_exporter_value_1,
+                                              sizeof(a12_expected_exporter_value_1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(a12_expected_exporter_value_2),
+                 .expected_exporter_value = { a12_expected_exporter_value_2,
+                                              sizeof(a12_expected_exporter_value_2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(a12_expected_exporter_value_3),
+                 .expected_exporter_value = { a12_expected_exporter_value_3,
+                                              sizeof(a12_expected_exporter_value_3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x78, 0x62, 0x8c, 0x35, 0x4e, 0x46, 0xf3, 0xe1,
+               0x69, 0xbd, 0x23, 0x1b, 0xe7, 0xb2, 0xff, 0x1c,
+               0x77, 0xaa, 0x30, 0x24, 0x60, 0xa2, 0x6d, 0xbf,
+               0xa1, 0x55, 0x15, 0x68, 0x4c, 0x00, 0x13, 0x0b
+       };
+       unsigned char ikmR[] = {
+               0xd4, 0xa0, 0x9d, 0x09, 0xf5, 0x75, 0xfe, 0xf4,
+               0x25, 0x90, 0x5d, 0x2a, 0xb3, 0x96, 0xc1, 0x44,
+               0x91, 0x41, 0x46, 0x3f, 0x69, 0x8f, 0x8e, 0xfd,
+               0xb7, 0xac, 0xcf, 0xaf, 0xf8, 0x99, 0x50, 0x98
+       };
+
+       unsigned char expected_enc[] = { 0x0a, 0xd0, 0x95, 0x0d, 0x9f, 0xb9,
+                                        0x58, 0x8e, 0x59, 0x69, 0x0b, 0x74,
+                                        0xf1, 0x23, 0x7e, 0xcd, 0xf1, 0xd7,
+                                        0x75, 0xcd, 0x60, 0xbe, 0x2e, 0xca,
+                                        0x57, 0xaf, 0x5a, 0x4b, 0x04, 0x71,
+                                        0xc9, 0x1b };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a13(void)
+{
+       unsigned char a13_seq0_expected_ct[] = {
+               0x5f, 0xd9, 0x2c, 0xc9, 0xd4, 0x6d, 0xbf, 0x89, 0x43,
+               0xe7, 0x2a, 0x07, 0xe4, 0x2f, 0x36, 0x3e, 0xd5, 0xf7,
+               0x21, 0x21, 0x2c, 0xd9, 0x0b, 0xcf, 0xd0, 0x72, 0xbf,
+               0xd9, 0xf4, 0x4e, 0x06, 0xb8, 0x0f, 0xd1, 0x78, 0x24,
+               0x94, 0x74, 0x96, 0xe2, 0x1b, 0x68, 0x0c, 0x14, 0x1b
+       };
+
+       unsigned char a13_seq1_expected_ct[] = {
+               0xd3, 0x73, 0x6b, 0xb2, 0x56, 0xc1, 0x9b, 0xfa, 0x93,
+               0xd7, 0x9e, 0x8f, 0x80, 0xb7, 0x97, 0x12, 0x62, 0xcb,
+               0x7c, 0x88, 0x7e, 0x35, 0xc2, 0x63, 0x70, 0xcf, 0xed,
+               0x62, 0x25, 0x43, 0x69, 0xa1, 0xb5, 0x2e, 0x3d, 0x50,
+               0x5b, 0x79, 0xdd, 0x69, 0x9f, 0x00, 0x2b, 0xc8, 0xed
+       };
+
+       unsigned char a13_seq2_expected_ct[] = {
+               0x12, 0x21, 0x75, 0xcf, 0xd5, 0x67, 0x8e, 0x04, 0x89,
+               0x4e, 0x4f, 0xf8, 0x78, 0x9e, 0x85, 0xdd, 0x38, 0x1d,
+               0xf4, 0x8d, 0xca, 0xf9, 0x70, 0xd5, 0x20, 0x57, 0xdf,
+               0x2c, 0x9a, 0xcc, 0x3b, 0x12, 0x13, 0x13, 0xa2, 0xbf,
+               0xea, 0xa9, 0x86, 0x05, 0x0f, 0x82, 0xd9, 0x36, 0x45
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { a13_seq0_expected_ct,
+                                          sizeof(a13_seq0_expected_ct) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { a13_seq1_expected_ct,
+                                          sizeof(a13_seq1_expected_ct) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { a13_seq2_expected_ct,
+                                          sizeof(a13_seq2_expected_ct) } }
+       };
+
+       unsigned char a13_expected_exporter_value_1[] = {
+               0x28, 0xc7, 0x00, 0x88, 0x01, 0x7d, 0x70, 0xc8,
+               0x96, 0xa8, 0x42, 0x0f, 0x04, 0x70, 0x2c, 0x5a,
+               0x32, 0x1d, 0x9c, 0xbf, 0x02, 0x79, 0xfb, 0xa8,
+               0x99, 0xb5, 0x9e, 0x51, 0xba, 0xc7, 0x2c, 0x85
+       };
+
+       unsigned char a13_expected_exporter_value_2[] = {
+               0x25, 0xdf, 0xc0, 0x04, 0xb0, 0x89, 0x2b, 0xe1,
+               0x88, 0x8c, 0x39, 0x14, 0x97, 0x7a, 0xa9, 0xc9,
+               0xbb, 0xaf, 0x2c, 0x74, 0x71, 0x70, 0x8a, 0x49,
+               0xe1, 0x19, 0x5a, 0xf4, 0x8a, 0x6f, 0x29, 0xce
+       };
+
+       unsigned char a13_expected_exporter_value_3[] = {
+               0x5a, 0x01, 0x31, 0x81, 0x3a, 0xbc, 0x9a, 0x52,
+               0x2c, 0xad, 0x67, 0x8e, 0xb6, 0xba, 0xfa, 0xab,
+               0xc4, 0x33, 0x89, 0x93, 0x4a, 0xdb, 0x80, 0x97,
+               0xd2, 0x3c, 0x5f, 0xf6, 0x80, 0x59, 0xeb, 0x64
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(a13_expected_exporter_value_1),
+                 .expected_exporter_value = { a13_expected_exporter_value_1,
+                                              sizeof(a13_expected_exporter_value_1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(a13_expected_exporter_value_2),
+                 .expected_exporter_value = { a13_expected_exporter_value_2,
+                                              sizeof(a13_expected_exporter_value_2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(a13_expected_exporter_value_3),
+                 .expected_exporter_value = { a13_expected_exporter_value_3,
+                                              sizeof(a13_expected_exporter_value_3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x6e, 0x6d, 0x8f, 0x20, 0x0e, 0xa2, 0xfb, 0x20,
+               0xc3, 0x0b, 0x00, 0x3a, 0x8b, 0x4f, 0x43, 0x3d,
+               0x2f, 0x4e, 0xd4, 0xc2, 0x65, 0x8d, 0x5b, 0xc8,
+               0xce, 0x2f, 0xef, 0x71, 0x80, 0x59, 0xc9, 0xf7
+       };
+
+       unsigned char ikmR[] = {
+               0xf1, 0xd4, 0xa3, 0x0a, 0x4c, 0xef, 0x8d, 0x6d,
+               0x4e, 0x3b, 0x01, 0x6e, 0x6f, 0xd3, 0x79, 0x9e,
+               0xa0, 0x57, 0xdb, 0x4f, 0x34, 0x54, 0x72, 0xed,
+               0x30, 0x2a, 0x67, 0xce, 0x1c, 0x20, 0xcd, 0xec
+       };
+
+       unsigned char ikmS[] = {
+               0x94, 0xb0, 0x20, 0xce, 0x91, 0xd7, 0x3f, 0xca,
+               0x46, 0x49, 0x00, 0x6c, 0x7e, 0x73, 0x29, 0xa6,
+               0x7b, 0x40, 0xc5, 0x5e, 0x9e, 0x93, 0xcc, 0x90,
+               0x7d, 0x28, 0x2b, 0xbb, 0xff, 0x38, 0x6f, 0x58
+       };
+
+       unsigned char expected_enc[] = { 0x23, 0xfb, 0x95, 0x25, 0x71, 0xa1,
+                                        0x4a, 0x25, 0xe3, 0xd6, 0x78, 0x14,
+                                        0x0c, 0xd0, 0xe5, 0xeb, 0x47, 0xa0,
+                                        0x96, 0x1b, 0xb1, 0x8a, 0xfc, 0xf8,
+                                        0x58, 0x96, 0xe5, 0x45, 0x3c, 0x31,
+                                        0x2e, 0x76 };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a14(void)
+{
+       unsigned char seq0_expected_ct[] = {
+               0xa8, 0x4c, 0x64, 0xdf, 0x1e, 0x11, 0xd8, 0xfd, 0x11,
+               0x45, 0x00, 0x39, 0xd4, 0xfe, 0x64, 0xff, 0x0c, 0x8a,
+               0x99, 0xfc, 0xa0, 0xbd, 0x72, 0xc2, 0xd4, 0xc3, 0xe0,
+               0x40, 0x0b, 0xc1, 0x4a, 0x40, 0xf2, 0x7e, 0x45, 0xe1,
+               0x41, 0xa2, 0x40, 0x01, 0x69, 0x77, 0x37, 0x53, 0x3e
+       };
+
+       unsigned char seq1_expected_ct[] = {
+               0x4d, 0x19, 0x30, 0x3b, 0x84, 0x8f, 0x42, 0x4f, 0xc3,
+               0xc3, 0xbe, 0xca, 0x24, 0x9b, 0x2c, 0x6d, 0xe0, 0xa3,
+               0x40, 0x83, 0xb8, 0xe9, 0x09, 0xb6, 0xaa, 0x4c, 0x36,
+               0x88, 0x50, 0x5c, 0x05, 0xff, 0xe0, 0xc8, 0xf5, 0x7a,
+               0x0a, 0x4c, 0x5a, 0xb9, 0xda, 0x12, 0x74, 0x35, 0xd9
+       };
+
+       unsigned char seq2_expected_ct[] = {
+               0x0c, 0x08, 0x5a, 0x36, 0x5f, 0xbf, 0xa6, 0x34, 0x09,
+               0x94, 0x3b, 0x00, 0xa3, 0x12, 0x7a, 0xbc, 0xe6, 0xe4,
+               0x59, 0x91, 0xbc, 0x65, 0x3f, 0x18, 0x2a, 0x80, 0x12,
+               0x08, 0x68, 0xfc, 0x50, 0x7e, 0x9e, 0x4d, 0x5e, 0x37,
+               0xbc, 0xc3, 0x84, 0xfc, 0x8f, 0x14, 0x15, 0x3b, 0x24
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_expected_ct,
+                                          sizeof(seq0_expected_ct) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_expected_ct,
+                                          sizeof(seq1_expected_ct) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_expected_ct,
+                                          sizeof(seq2_expected_ct) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x08, 0xf7, 0xe2, 0x06, 0x44, 0xbb, 0x9b, 0x8a,
+               0xf5, 0x4a, 0xd6, 0x6d, 0x20, 0x67, 0x45, 0x7c,
+               0x5f, 0x9f, 0xcb, 0x2a, 0x23, 0xd9, 0xf6, 0xcb,
+               0x44, 0x45, 0xc0, 0x79, 0x7b, 0x33, 0x00, 0x67
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x52, 0xe5, 0x1f, 0xf7, 0xd4, 0x36, 0x55, 0x7c,
+               0xed, 0x52, 0x65, 0xff, 0x8b, 0x94, 0xce, 0x69,
+               0xcf, 0x75, 0x83, 0xf4, 0x9c, 0xdb, 0x37, 0x4e,
+               0x6a, 0xad, 0x80, 0x1f, 0xc0, 0x63, 0xb0, 0x10
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xa3, 0x0c, 0x20, 0x37, 0x0c, 0x02, 0x6b, 0xbe,
+               0xa4, 0xdc, 0xa5, 0x1c, 0xb6, 0x37, 0x61, 0x69,
+               0x51, 0x32, 0xd3, 0x42, 0xba, 0xe3, 0x3a, 0x6a,
+               0x11, 0x52, 0x7d, 0x3e, 0x76, 0x79, 0x43, 0x6d
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x43, 0x03, 0x61, 0x90, 0x85, 0xa2, 0x0e, 0xbc,
+               0xf1, 0x8e, 0xdd, 0x22, 0x78, 0x29, 0x52, 0xb8,
+               0xa7, 0x16, 0x1e, 0x1d, 0xba, 0xe6, 0xe4, 0x6e,
+               0x14, 0x3a, 0x52, 0xa9, 0x61, 0x27, 0xcf, 0x84
+       };
+
+       unsigned char ikmR[] = {
+               0x4b, 0x16, 0x22, 0x1f, 0x3b, 0x26, 0x9a, 0x88,
+               0xe2, 0x07, 0x27, 0x0b, 0x5e, 0x1d, 0xe2, 0x8c,
+               0xb0, 0x1f, 0x84, 0x78, 0x41, 0xb3, 0x44, 0xb8,
+               0x31, 0x4d, 0x6a, 0x62, 0x2f, 0xe5, 0xee, 0x90
+       };
+
+       unsigned char ikmS[] = {
+               0x62, 0xf7, 0x7d, 0xcf, 0x5d, 0xf0, 0xdd, 0x7e,
+               0xac, 0x54, 0xea, 0xc9, 0xf6, 0x54, 0xf4, 0x26,
+               0xd4, 0x16, 0x1e, 0xc8, 0x50, 0xcc, 0x65, 0xc5,
+               0x4f, 0x8b, 0x65, 0xd2, 0xe0, 0xb4, 0xe3, 0x45
+       };
+
+       unsigned char expected_enc[] = { 0x82, 0x08, 0x18, 0xd3, 0xc2, 0x39,
+                                        0x93, 0x49, 0x2c, 0xc5, 0x62, 0x3a,
+                                        0xb4, 0x37, 0xa4, 0x8a, 0x0a, 0x7c,
+                                        0xa3, 0xe9, 0x63, 0x9c, 0x14, 0x0f,
+                                        0xe1, 0xe3, 0x38, 0x11, 0xeb, 0x84,
+                                        0x4b, 0x7c };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a21(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x1c, 0x52, 0x50, 0xd8, 0x03, 0x4e, 0xc2, 0xb7, 0x84,
+               0xba, 0x2c, 0xfd, 0x69, 0xdb, 0xdb, 0x8a, 0xf4, 0x06,
+               0xcf, 0xe3, 0xff, 0x93, 0x8e, 0x13, 0x1f, 0x0d, 0xef,
+               0x8c, 0x8b, 0x60, 0xb4, 0xdb, 0x21, 0x99, 0x3c, 0x62,
+               0xce, 0x81, 0x88, 0x3d, 0x2d, 0xd1, 0xb5, 0x1a, 0x28
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x6b, 0x53, 0xc0, 0x51, 0xe4, 0x19, 0x9c, 0x51, 0x8d,
+               0xe7, 0x95, 0x94, 0xe1, 0xc4, 0xab, 0x18, 0xb9, 0x6f,
+               0x08, 0x15, 0x49, 0xd4, 0x5c, 0xe0, 0x15, 0xbe, 0x00,
+               0x20, 0x90, 0xbb, 0x11, 0x9e, 0x85, 0x28, 0x53, 0x37,
+               0xcc, 0x95, 0xba, 0x5f, 0x59, 0x99, 0x2d, 0xc9, 0x8c
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x71, 0x14, 0x6b, 0xd6, 0x79, 0x5c, 0xcc, 0x9c, 0x49,
+               0xce, 0x25, 0xdd, 0xa1, 0x12, 0xa4, 0x8f, 0x20, 0x2a,
+               0xd2, 0x20, 0x55, 0x95, 0x02, 0xce, 0xf1, 0xf3, 0x42,
+               0x71, 0xe0, 0xcb, 0x4b, 0x02, 0xb4, 0xf1, 0x0e, 0xca,
+               0xc6, 0xf4, 0x8c, 0x32, 0xf8, 0x78, 0xfa, 0xe8, 0x6b
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x4b, 0xbd, 0x62, 0x43, 0xb8, 0xbb, 0x54, 0xce,
+               0xc3, 0x11, 0xfa, 0xc9, 0xdf, 0x81, 0x84, 0x1b,
+               0x6f, 0xd6, 0x1f, 0x56, 0x53, 0x8a, 0x77, 0x5e,
+               0x7c, 0x80, 0xa9, 0xf4, 0x01, 0x60, 0x60, 0x6e
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x8c, 0x1d, 0xf1, 0x47, 0x32, 0x58, 0x0e, 0x55,
+               0x01, 0xb0, 0x0f, 0x82, 0xb1, 0x0a, 0x16, 0x47,
+               0xb4, 0x07, 0x13, 0x19, 0x1b, 0x7c, 0x12, 0x40,
+               0xac, 0x80, 0xe2, 0xb6, 0x88, 0x08, 0xba, 0x69
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x5a, 0xcb, 0x09, 0x21, 0x11, 0x39, 0xc4, 0x3b,
+               0x30, 0x90, 0x48, 0x9a, 0x9d, 0xa4, 0x33, 0xe8,
+               0xa3, 0x0e, 0xe7, 0x18, 0x8b, 0xa8, 0xb0, 0xa9,
+               0xa1, 0xcc, 0xf0, 0xc2, 0x29, 0x28, 0x3e, 0x53
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x90, 0x9a, 0x9b, 0x35, 0xd3, 0xdc, 0x47, 0x13,
+               0xa5, 0xe7, 0x2a, 0x4d, 0xa2, 0x74, 0xb5, 0x5d,
+               0x3d, 0x38, 0x21, 0xa3, 0x7e, 0x5d, 0x09, 0x9e,
+               0x74, 0xa6, 0x47, 0xdb, 0x58, 0x3a, 0x90, 0x4b
+       };
+
+       unsigned char ikmR[] = {
+               0x1a, 0xc0, 0x1f, 0x18, 0x1f, 0xdf, 0x9f, 0x35,
+               0x27, 0x97, 0x65, 0x51, 0x61, 0xc5, 0x8b, 0x75,
+               0xc6, 0x56, 0xa6, 0xcc, 0x27, 0x16, 0xdc, 0xb6,
+               0x63, 0x72, 0xda, 0x83, 0x55, 0x42, 0xe1, 0xdf
+       };
+
+       unsigned char expected_enc[] = { 0x1a, 0xfa, 0x08, 0xd3, 0xde, 0xc0,
+                                        0x47, 0xa6, 0x43, 0x88, 0x51, 0x63,
+                                        0xf1, 0x18, 0x04, 0x76, 0xfa, 0x7d,
+                                        0xdb, 0x54, 0xc6, 0xa8, 0x02, 0x9e,
+                                        0xa3, 0x3f, 0x95, 0x79, 0x6b, 0xf2,
+                                        0xac, 0x4a };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a22(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x4a, 0x17, 0x7f, 0x9c, 0x0d, 0x6f, 0x15, 0xcf, 0xdf,
+               0x53, 0x3f, 0xb6, 0x5b, 0xf8, 0x4a, 0xec, 0xdc, 0x6a,
+               0xb1, 0x6b, 0x8b, 0x85, 0xb4, 0xcf, 0x65, 0xa3, 0x70,
+               0xe0, 0x7f, 0xc1, 0xd7, 0x8d, 0x28, 0xfb, 0x07, 0x32,
+               0x14, 0x52, 0x52, 0x76, 0xf4, 0xa8, 0x96, 0x08, 0xff
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x5c, 0x3c, 0xab, 0xae, 0x2f, 0x0b, 0x3e, 0x12, 0x4d,
+               0x8d, 0x86, 0x4c, 0x11, 0x6f, 0xd8, 0xf2, 0x0f, 0x3f,
+               0x56, 0xfd, 0xa9, 0x88, 0xc3, 0x57, 0x3b, 0x40, 0xb0,
+               0x99, 0x97, 0xfd, 0x6c, 0x76, 0x9e, 0x77, 0xc8, 0xed,
+               0xa6, 0xcd, 0xa4, 0xf9, 0x47, 0xf5, 0xb7, 0x04, 0xa8
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x14, 0x95, 0x89, 0x00, 0xb4, 0x4b, 0xda, 0xe9, 0xcb,
+               0xe5, 0xa5, 0x28, 0xbf, 0x93, 0x3c, 0x5c, 0x99, 0x0d,
+               0xbb, 0x8e, 0x28, 0x2e, 0x6e, 0x49, 0x5a, 0xdf, 0x82,
+               0x05, 0xd1, 0x9d, 0xa9, 0xeb, 0x27, 0x0e, 0x3a, 0x6f,
+               0x1e, 0x06, 0x13, 0xab, 0x7e, 0x75, 0x79, 0x62, 0xa4
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x81, 0x3c, 0x1b, 0xfc, 0x51, 0x6c, 0x99, 0x07,
+               0x6a, 0xe0, 0xf4, 0x66, 0x67, 0x1f, 0x0b, 0xa5,
+               0xff, 0x24, 0x4a, 0x41, 0x69, 0x9f, 0x7b, 0x24,
+               0x17, 0xe4, 0xc5, 0x9d, 0x46, 0xd3, 0x9f, 0x40
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x27, 0x45, 0xcf, 0x3d, 0x5b, 0xb6, 0x5c, 0x33,
+               0x36, 0x58, 0x73, 0x29, 0x54, 0xee, 0x7a, 0xf4,
+               0x9e, 0xb8, 0x95, 0xce, 0x77, 0xf8, 0x02, 0x28,
+               0x73, 0xa6, 0x2a, 0x13, 0xc9, 0x4c, 0xb4, 0xe1
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xad, 0x40, 0xe3, 0xae, 0x14, 0xf2, 0x1c, 0x99,
+               0xbf, 0xde, 0xbc, 0x20, 0xae, 0x14, 0xab, 0x86,
+               0xf4, 0xca, 0x2d, 0xc9, 0xa4, 0x79, 0x9d, 0x20,
+               0x0f, 0x43, 0xa2, 0x5f, 0x99, 0xfa, 0x78, 0xae
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x35, 0x70, 0x6a, 0x0b, 0x09, 0xfb, 0x26, 0xfb,
+               0x45, 0xc3, 0x9c, 0x2f, 0x50, 0x79, 0xc7, 0x09,
+               0xc7, 0xcf, 0x98, 0xe4, 0x3a, 0xfa, 0x97, 0x3f,
+               0x14, 0xd8, 0x8e, 0xce, 0x7e, 0x29, 0xc2, 0xe3
+       };
+
+       unsigned char ikmR[] = {
+               0x26, 0xb9, 0x23, 0xea, 0xde, 0x72, 0x94, 0x1c,
+               0x8a, 0x85, 0xb0, 0x99, 0x86, 0xcd, 0xfa, 0x3f,
+               0x12, 0x96, 0x85, 0x22, 0x61, 0xad, 0xed, 0xc5,
+               0x2d, 0x58, 0xd2, 0x93, 0x02, 0x69, 0x81, 0x2b
+       };
+
+       unsigned char expected_enc[] = { 0x22, 0x61, 0x29, 0x9c, 0x3f, 0x40,
+                                        0xa9, 0xaf, 0xc1, 0x33, 0xb9, 0x69,
+                                        0xa9, 0x7f, 0x05, 0xe9, 0x5b, 0xe2,
+                                        0xc5, 0x14, 0xe5, 0x4f, 0x3d, 0xe2,
+                                        0x6c, 0xbe, 0x56, 0x44, 0xac, 0x73,
+                                        0x5b, 0x04 };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a23(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0xab, 0x1a, 0x13, 0xc9, 0xd4, 0xf0, 0x1a, 0x87, 0xec,
+               0x34, 0x40, 0xdb, 0xd7, 0x56, 0xe2, 0x67, 0x7b, 0xd2,
+               0xec, 0xf9, 0xdf, 0x0c, 0xe7, 0xed, 0x73, 0x86, 0x9b,
+               0x98, 0xe0, 0x0c, 0x09, 0xbe, 0x11, 0x1c, 0xb9, 0xfd,
+               0xf0, 0x77, 0x34, 0x7a, 0xeb, 0x88, 0xe6, 0x1b, 0xdf
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x32, 0x65, 0xc7, 0x80, 0x7f, 0xff, 0xf7, 0xfd, 0xac,
+               0xe2, 0x16, 0x59, 0xa2, 0xc6, 0xcc, 0xff, 0xee, 0x52,
+               0xa2, 0x6d, 0x27, 0x0c, 0x76, 0x46, 0x8e, 0xd7, 0x42,
+               0x02, 0xa6, 0x54, 0x78, 0xbf, 0xae, 0xdf, 0xff, 0x9c,
+               0x2b, 0x76, 0x34, 0xe2, 0x4f, 0x10, 0xb7, 0x10, 0x16
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x3a, 0xad, 0xee, 0x86, 0xad, 0x2a, 0x05, 0x08, 0x1e,
+               0xa8, 0x60, 0x03, 0x3a, 0x9d, 0x09, 0xdb, 0xcc, 0xb4,
+               0xac, 0xac, 0x2d, 0xed, 0x08, 0x91, 0xda, 0x40, 0xf5,
+               0x1d, 0x4d, 0xf1, 0x99, 0x25, 0xf7, 0xa7, 0x67, 0xb0,
+               0x76, 0xa5, 0xcb, 0xc9, 0x35, 0x5c, 0x8f, 0xd3, 0x5e
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x07, 0x0c, 0xff, 0xaf, 0xd8, 0x9b, 0x67, 0xb7,
+               0xf0, 0xee, 0xb8, 0x00, 0x23, 0x53, 0x03, 0xa2,
+               0x23, 0xe6, 0xff, 0x9d, 0x1e, 0x77, 0x4d, 0xce,
+               0x8e, 0xac, 0x58, 0x5c, 0x86, 0x88, 0xc8, 0x72
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x28, 0x52, 0xe7, 0x28, 0x56, 0x8d, 0x40, 0xdd,
+               0xb0, 0xed, 0xde, 0x28, 0x4d, 0x36, 0xa4, 0x35,
+               0x9c, 0x56, 0x55, 0x8b, 0xb2, 0xfb, 0x88, 0x37,
+               0xcd, 0x3d, 0x92, 0xe4, 0x6a, 0x3a, 0x14, 0xa8
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x1d, 0xf3, 0x9d, 0xc5, 0xdd, 0x60, 0xed, 0xcb,
+               0xf5, 0xf9, 0xae, 0x80, 0x4e, 0x15, 0xad, 0xa6,
+               0x6e, 0x88, 0x5b, 0x28, 0xed, 0x79, 0x29, 0x11,
+               0x6f, 0x76, 0x83, 0x69, 0xa3, 0xf9, 0x50, 0xee
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x93, 0x8d, 0x3d, 0xaa, 0x5a, 0x89, 0x04, 0x54,
+               0x0b, 0xc2, 0x4f, 0x48, 0xae, 0x90, 0xee, 0xd3,
+               0xf4, 0xf7, 0xf1, 0x18, 0x39, 0x56, 0x05, 0x97,
+               0xb5, 0x5e, 0x7c, 0x95, 0x98, 0xc9, 0x96, 0xc0
+       };
+
+       unsigned char ikmR[] = {
+               0x64, 0x83, 0x5d, 0x5e, 0xe6, 0x4a, 0xa7, 0xaa,
+               0xd5, 0x7c, 0x6f, 0x2e, 0x4f, 0x75, 0x8f, 0x76,
+               0x96, 0x61, 0x7f, 0x88, 0x29, 0xe7, 0x0b, 0xc9,
+               0xac, 0x7a, 0x5e, 0xf9, 0x5d, 0x1c, 0x75, 0x6c
+       };
+
+       unsigned char ikmS[] = {
+               0x9d, 0x8f, 0x94, 0x53, 0x7d, 0x5a, 0x3d, 0xde,
+               0xf7, 0x12, 0x34, 0xc0, 0xba, 0xed, 0xfa, 0xd4,
+               0xca, 0x68, 0x61, 0x63, 0x4d, 0x0b, 0x94, 0xc3,
+               0x00, 0x7f, 0xed, 0x55, 0x7a, 0xd1, 0x7d, 0xf6
+       };
+
+       unsigned char expected_enc[] = { 0xf7, 0x67, 0x4c, 0xc8, 0xcd, 0x7b,
+                                        0xaa, 0x58, 0x72, 0xd1, 0xf3, 0x3d,
+                                        0xba, 0xff, 0xe3, 0x31, 0x42, 0x39,
+                                        0xf6, 0x19, 0x7d, 0xdf, 0x5d, 0xed,
+                                        0x17, 0x46, 0x76, 0x0b, 0xfc, 0x84,
+                                        0x7e, 0x0e };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a24(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x9a, 0xa5, 0x2e, 0x29, 0x27, 0x4f, 0xc6, 0x17, 0x2e,
+               0x38, 0xa4, 0x46, 0x13, 0x61, 0xd2, 0x34, 0x25, 0x85,
+               0xd3, 0xae, 0xec, 0x67, 0xfb, 0x3b, 0x72, 0x1e, 0xcd,
+               0x63, 0xf0, 0x59, 0x57, 0x7c, 0x7f, 0xe8, 0x86, 0xbe,
+               0x0e, 0xde, 0x01, 0x45, 0x6e, 0xbc, 0x67, 0xd5, 0x97
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x59, 0x46, 0x0b, 0xac, 0xdb, 0xe7, 0xa9, 0x20, 0xef,
+               0x28, 0x06, 0xa7, 0x49, 0x37, 0xd5, 0xa6, 0x91, 0xd6,
+               0xd5, 0x06, 0x2d, 0x7d, 0xaa, 0xfc, 0xad, 0x7d, 0xb7,
+               0xe4, 0xd8, 0xc6, 0x49, 0xad, 0xff, 0xe5, 0x75, 0xc1,
+               0x88, 0x9c, 0x5c, 0x2e, 0x3a, 0x49, 0xaf, 0x8e, 0x3e
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x56, 0x88, 0xff, 0x6a, 0x03, 0xba, 0x26, 0xae, 0x93,
+               0x60, 0x44, 0xa5, 0xc8, 0x00, 0xf2, 0x86, 0xfb, 0x5d,
+               0x1e, 0xcc, 0xdd, 0x2a, 0x0f, 0x26, 0x8f, 0x6f, 0xf9,
+               0x77, 0x3b, 0x51, 0x16, 0x93, 0x18, 0xd1, 0xa1, 0x46,
+               0x6b, 0xb3, 0x62, 0x63, 0x41, 0x50, 0x71, 0xdb, 0x00
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0xc2, 0x3e, 0xbd, 0x4e, 0x7a, 0x0a, 0xd0, 0x6a,
+               0x5d, 0xdd, 0xf7, 0x79, 0xf6, 0x50, 0x04, 0xce,
+               0x94, 0x81, 0x06, 0x9c, 0xe0, 0xf0, 0xe6, 0xdd,
+               0x51, 0xa0, 0x45, 0x39, 0xdd, 0xcb, 0xd5, 0xcd
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0xed, 0x7f, 0xf5, 0xca, 0x40, 0xa3, 0xd8, 0x45,
+               0x61, 0x06, 0x7e, 0xbc, 0x8e, 0x01, 0x70, 0x2b,
+               0xc3, 0x6c, 0xf1, 0xeb, 0x99, 0xd4, 0x2a, 0x92,
+               0x00, 0x46, 0x42, 0xb9, 0xdf, 0xaa, 0xdd, 0x37
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xd3, 0xba, 0xe0, 0x66, 0xaa, 0x8d, 0xa2, 0x7d,
+               0x52, 0x7d, 0x85, 0xc0, 0x40, 0xf7, 0xdd, 0x6c,
+               0xcb, 0x60, 0x22, 0x1c, 0x90, 0x2e, 0xe3, 0x6a,
+               0x82, 0xf7, 0x0b, 0xcd, 0x62, 0xa6, 0x0e, 0xe4
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x49, 0xd6, 0xea, 0xc8, 0xc6, 0xc5, 0x58, 0xc9,
+               0x53, 0xa0, 0xa2, 0x52, 0x92, 0x9a, 0x81, 0x87,
+               0x45, 0xbb, 0x08, 0xcd, 0x3d, 0x29, 0xe1, 0x5f,
+               0x9f, 0x5d, 0xb5, 0xeb, 0x2e, 0x7d, 0x4b, 0x84
+       };
+
+       unsigned char ikmR[] = {
+               0xf3, 0x30, 0x4d, 0xdc, 0xf1, 0x58, 0x48, 0x48,
+               0x82, 0x71, 0xf1, 0x2b, 0x75, 0xec, 0xaf, 0x72,
+               0x30, 0x1f, 0xaa, 0xbf, 0x6a, 0xd2, 0x83, 0x65,
+               0x4a, 0x14, 0xc3, 0x98, 0x83, 0x2e, 0xb1, 0x84
+       };
+
+       unsigned char ikmS[] = {
+               0x20, 0xad, 0xe1, 0xd5, 0x20, 0x3d, 0xe1, 0xaa,
+               0xdf, 0xb2, 0x61, 0xc4, 0x70, 0x0b, 0x64, 0x32,
+               0xe2, 0x60, 0xd0, 0xd3, 0x17, 0xbe, 0x6e, 0xbb,
+               0xb8, 0xd7, 0xff, 0xfb, 0x1f, 0x86, 0xad, 0x9d
+       };
+
+       unsigned char expected_enc[] = { 0x65, 0x6a, 0x2e, 0x00, 0xdc, 0x99,
+                                        0x90, 0xfd, 0x18, 0x9e, 0x6e, 0x47,
+                                        0x34, 0x59, 0x39, 0x2d, 0xf5, 0x56,
+                                        0xe9, 0xa2, 0x75, 0x87, 0x54, 0xa0,
+                                        0x9d, 0xb3, 0xf5, 0x11, 0x79, 0xa3,
+                                        0xfc, 0x02 };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a31(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x5a, 0xd5, 0x90, 0xbb, 0x8b, 0xaa, 0x57, 0x7f, 0x86,
+               0x19, 0xdb, 0x35, 0xa3, 0x63, 0x11, 0x22, 0x6a, 0x89,
+               0x6e, 0x73, 0x42, 0xa6, 0xd8, 0x36, 0xd8, 0xb7, 0xbc,
+               0xd2, 0xf2, 0x0b, 0x6c, 0x7f, 0x90, 0x76, 0xac, 0x23,
+               0x2e, 0x3a, 0xb2, 0x52, 0x3f, 0x39, 0x51, 0x34, 0x34
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xfa, 0x6f, 0x03, 0x7b, 0x47, 0xfc, 0x21, 0x82, 0x6b,
+               0x61, 0x01, 0x72, 0xca, 0x96, 0x37, 0xe8, 0x2d, 0x6e,
+               0x58, 0x01, 0xeb, 0x31, 0xcb, 0xd3, 0x74, 0x82, 0x71,
+               0xaf, 0xfd, 0x4e, 0xcb, 0x06, 0x64, 0x6e, 0x03, 0x29,
+               0xcb, 0xdf, 0x3c, 0x3c, 0xd6, 0x55, 0xb2, 0x8e, 0x82
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x89, 0x5c, 0xab, 0xfa, 0xc5, 0x0c, 0xe6, 0xc6, 0xeb,
+               0x02, 0xff, 0xe6, 0xc0, 0x48, 0xbf, 0x53, 0xb7, 0xf7,
+               0xbe, 0x9a, 0x91, 0xfc, 0x55, 0x94, 0x02, 0xcb, 0xc5,
+               0xb8, 0xdc, 0xae, 0xb5, 0x2b, 0x2c, 0xcc, 0x93, 0xe4,
+               0x66, 0xc2, 0x8f, 0xb5, 0x5f, 0xed, 0x7a, 0x7f, 0xec
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x5e, 0x9b, 0xc3, 0xd2, 0x36, 0xe1, 0x91, 0x1d,
+               0x95, 0xe6, 0x5b, 0x57, 0x6a, 0x8a, 0x86, 0xd4,
+               0x78, 0xfb, 0x82, 0x7e, 0x8b, 0xdf, 0xe7, 0x7b,
+               0x74, 0x1b, 0x28, 0x98, 0x90, 0x49, 0x0d, 0x4d
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x6c, 0xff, 0x87, 0x65, 0x89, 0x31, 0xbd, 0xa8,
+               0x3d, 0xc8, 0x57, 0xe6, 0x35, 0x3e, 0xfe, 0x49,
+               0x87, 0xa2, 0x01, 0xb8, 0x49, 0x65, 0x8d, 0x9b,
+               0x04, 0x7a, 0xab, 0x4c, 0xf2, 0x16, 0xe7, 0x96
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xd8, 0xf1, 0xea, 0x79, 0x42, 0xad, 0xbb, 0xa7,
+               0x41, 0x2c, 0x6d, 0x43, 0x1c, 0x62, 0xd0, 0x13,
+               0x71, 0xea, 0x47, 0x6b, 0x82, 0x3e, 0xb6, 0x97,
+               0xe1, 0xf6, 0xe6, 0xca, 0xe1, 0xda, 0xb8, 0x5a
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x42, 0x70, 0xe5, 0x4f, 0xfd, 0x08, 0xd7, 0x9d,
+               0x59, 0x28, 0x02, 0x0a, 0xf4, 0x68, 0x6d, 0x8f,
+               0x6b, 0x7d, 0x35, 0xdb, 0xe4, 0x70, 0x26, 0x5f,
+               0x1f, 0x5a, 0xa2, 0x28, 0x16, 0xce, 0x86, 0x0e
+       };
+
+       unsigned char ikmR[] = {
+               0x66, 0x8b, 0x37, 0x17, 0x1f, 0x10, 0x72, 0xf3,
+               0xcf, 0x12, 0xea, 0x8a, 0x23, 0x6a, 0x45, 0xdf,
+               0x23, 0xfc, 0x13, 0xb8, 0x2a, 0xf3, 0x60, 0x9a,
+               0xd1, 0xe3, 0x54, 0xf6, 0xef, 0x81, 0x75, 0x50
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0xa9, 0x27, 0x19, 0xc6, 0x19, 0x5d, 0x50, 0x85, 0x10,
+               0x4f, 0x46, 0x9a, 0x8b, 0x98, 0x14, 0xd5, 0x83, 0x8f, 0xf7,
+               0x2b, 0x60, 0x50, 0x1e, 0x2c, 0x44, 0x66, 0xe5, 0xe6, 0x7b,
+               0x32, 0x5a, 0xc9, 0x85, 0x36, 0xd7, 0xb6, 0x1a, 0x1a, 0xf4,
+               0xb7, 0x8e, 0x5b, 0x7f, 0x95, 0x1c, 0x09, 0x00, 0xbe, 0x86,
+               0x3c, 0x40, 0x3c, 0xe6, 0x5c, 0x9b, 0xfc, 0xb9, 0x38, 0x26,
+               0x57, 0x22, 0x2d, 0x18, 0xc4
+       };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a32(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x90, 0xc4, 0xde, 0xb5, 0xb7, 0x53, 0x18, 0x53, 0x01,
+               0x94, 0xe4, 0xbb, 0x62, 0xf8, 0x90, 0xb0, 0x19, 0xb1,
+               0x39, 0x7b, 0xbf, 0x9d, 0x0d, 0x6e, 0xb9, 0x18, 0x89,
+               0x0e, 0x1f, 0xb2, 0xbe, 0x1a, 0xc2, 0x60, 0x31, 0x93,
+               0xb6, 0x0a, 0x49, 0xc2, 0x12, 0x6b, 0x75, 0xd0, 0xeb
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x9e, 0x22, 0x33, 0x84, 0xa3, 0x62, 0x0f, 0x4a, 0x75,
+               0xb5, 0xa5, 0x2f, 0x54, 0x6b, 0x72, 0x62, 0xd8, 0x82,
+               0x6d, 0xea, 0x18, 0xdb, 0x5a, 0x36, 0x5f, 0xeb, 0x8b,
+               0x99, 0x71, 0x80, 0xb2, 0x2d, 0x72, 0xdc, 0x12, 0x87,
+               0xf7, 0x08, 0x9a, 0x10, 0x73, 0xa7, 0x10, 0x2c, 0x27
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0xad, 0xf9, 0xf6, 0x00, 0x07, 0x73, 0x03, 0x50, 0x23,
+               0xbe, 0x7d, 0x41, 0x5e, 0x13, 0xf8, 0x4c, 0x1c, 0xb3,
+               0x2a, 0x24, 0x33, 0x9a, 0x32, 0xeb, 0x81, 0xdf, 0x02,
+               0xbe, 0x9d, 0xdc, 0x6a, 0xbc, 0x88, 0x0d, 0xd8, 0x1c,
+               0xce, 0xb7, 0xc1, 0xd0, 0xc7, 0x78, 0x14, 0x65, 0xb2
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0xa1, 0x15, 0xa5, 0x9b, 0xf4, 0xdd, 0x8d, 0xc4,
+               0x93, 0x32, 0xd6, 0xa0, 0x09, 0x3a, 0xf8, 0xef,
+               0xca, 0x1b, 0xcb, 0xfd, 0x36, 0x27, 0xd8, 0x50,
+               0x17, 0x3f, 0x5c, 0x4a, 0x55, 0xd0, 0xc1, 0x85
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x45, 0x17, 0xea, 0xed, 0xe0, 0x66, 0x9b, 0x16,
+               0xaa, 0xc7, 0xc9, 0x2d, 0x57, 0x62, 0xdd, 0x45,
+               0x9c, 0x30, 0x1f, 0xa1, 0x0e, 0x02, 0x23, 0x7c,
+               0xd5, 0xae, 0xb9, 0xbe, 0x96, 0x94, 0x30, 0xc4
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x16, 0x4e, 0x02, 0x14, 0x4d, 0x44, 0xb6, 0x07,
+               0xa7, 0x72, 0x2e, 0x58, 0xb0, 0xf4, 0x15, 0x6e,
+               0x67, 0xc0, 0xc2, 0x87, 0x4d, 0x74, 0xcf, 0x71,
+               0xda, 0x6c, 0xa4, 0x8a, 0x4c, 0xbd, 0xc5, 0xe0
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x2a, 0xfa, 0x61, 0x1d, 0x8b, 0x1a, 0x7b, 0x32,
+               0x1c, 0x76, 0x1b, 0x48, 0x3b, 0x6a, 0x05, 0x35,
+               0x79, 0xaf, 0xa4, 0xf7, 0x67, 0x45, 0x0d, 0x3a,
+               0xd0, 0xf8, 0x4a, 0x39, 0xfd, 0xa5, 0x87, 0xa6
+       };
+
+       unsigned char ikmR[] = {
+               0xd4, 0x2e, 0xf8, 0x74, 0xc1, 0x91, 0x3d, 0x95,
+               0x68, 0xc9, 0x40, 0x54, 0x07, 0xc8, 0x05, 0xba,
+               0xdd, 0xaf, 0xfd, 0x08, 0x98, 0xa0, 0x0f, 0x1e,
+               0x84, 0xe1, 0x54, 0xfa, 0x78, 0x7b, 0x24, 0x29
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x30, 0x5d, 0x35, 0x56, 0x35, 0x27, 0xbc, 0xe0, 0x37,
+               0x77, 0x3d, 0x79, 0xa1, 0x3d, 0xea, 0xbe, 0xd0, 0xe8, 0xe7,
+               0xcd, 0xe6, 0x1e, 0xec, 0xee, 0x40, 0x34, 0x96, 0x95, 0x9e,
+               0x89, 0xe4, 0xd0, 0xca, 0x70, 0x17, 0x26, 0x69, 0x6d, 0x14,
+               0x85, 0x13, 0x7c, 0xcb, 0x53, 0x41, 0xb3, 0xc1, 0xc7, 0xaa,
+               0xee, 0x90, 0xa4, 0xa0, 0x24, 0x49, 0x72, 0x5e, 0x74, 0x4b,
+               0x11, 0x93, 0xb5, 0x3b, 0x5f
+       };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a33(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x82, 0xff, 0xc8, 0xc4, 0x47, 0x60, 0xdb, 0x69, 0x1a,
+               0x07, 0xc5, 0x62, 0x7e, 0x5f, 0xc2, 0xc0, 0x8e, 0x7a,
+               0x86, 0x97, 0x9e, 0xe7, 0x9b, 0x49, 0x4a, 0x17, 0xcc,
+               0x34, 0x05, 0x44, 0x6a, 0xc2, 0xbd, 0xb8, 0xf2, 0x65,
+               0xdb, 0x4a, 0x09, 0x9e, 0xd3, 0x28, 0x9f, 0xfe, 0x19
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xb0, 0xa7, 0x05, 0xa5, 0x45, 0x32, 0xc7, 0xb4, 0xf5,
+               0x90, 0x7d, 0xe5, 0x1c, 0x13, 0xdf, 0xfe, 0x1e, 0x08,
+               0xd5, 0x5e, 0xe9, 0xba, 0x59, 0x68, 0x61, 0x14, 0xb0,
+               0x59, 0x45, 0x49, 0x4d, 0x96, 0x72, 0x5b, 0x23, 0x94,
+               0x68, 0xf1, 0x22, 0x9e, 0x39, 0x66, 0xaa, 0x12, 0x50
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x8d, 0xc8, 0x05, 0x68, 0x0e, 0x32, 0x71, 0xa8, 0x01,
+               0x79, 0x08, 0x33, 0xed, 0x74, 0x47, 0x37, 0x10, 0x15,
+               0x76, 0x45, 0x58, 0x4f, 0x06, 0xd1, 0xb5, 0x3a, 0xd4,
+               0x39, 0x07, 0x8d, 0x88, 0x0b, 0x23, 0xe2, 0x52, 0x56,
+               0x66, 0x31, 0x78, 0x27, 0x1c, 0x80, 0xee, 0x8b, 0x7c
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x83, 0x7e, 0x49, 0xc3, 0xff, 0x62, 0x92, 0x50,
+               0xc8, 0xd8, 0x0d, 0x3c, 0x3f, 0xb9, 0x57, 0x72,
+               0x5e, 0xd4, 0x81, 0xe5, 0x9e, 0x2f, 0xeb, 0x57,
+               0xaf, 0xd9, 0xfe, 0x9a, 0x8c, 0x7c, 0x44, 0x97
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x59, 0x42, 0x13, 0xf9, 0x01, 0x8d, 0x61, 0x4b,
+               0x82, 0x00, 0x7a, 0x70, 0x21, 0xc3, 0x13, 0x5b,
+               0xda, 0x7b, 0x38, 0x0d, 0xa4, 0xac, 0xd9, 0xab,
+               0x27, 0x16, 0x5c, 0x50, 0x86, 0x40, 0xdb, 0xda
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x14, 0xfe, 0x63, 0x4f, 0x95, 0xca, 0x0d, 0x86,
+               0xe1, 0x52, 0x47, 0xcc, 0xa7, 0xde, 0x7b, 0xa9,
+               0xb7, 0x3c, 0x9b, 0x9d, 0xeb, 0x64, 0x37, 0xe1,
+               0xc8, 0x32, 0xda, 0xf7, 0x29, 0x1b, 0x79, 0xd5
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x79, 0x8d, 0x82, 0xa8, 0xd9, 0xea, 0x19, 0xdb,
+               0xc7, 0xf2, 0xc6, 0xdf, 0xa5, 0x4e, 0x8a, 0x67,
+               0x06, 0xf7, 0xcd, 0xc1, 0x19, 0xdb, 0x08, 0x13,
+               0xda, 0xcf, 0x84, 0x40, 0xab, 0x37, 0xc8, 0x57
+       };
+
+       unsigned char ikmR[] = {
+               0x7b, 0xc9, 0x3b, 0xde, 0x88, 0x90, 0xd1, 0xfb,
+               0x55, 0x22, 0x0e, 0x7f, 0x3b, 0x0c, 0x10, 0x7a,
+               0xe7, 0xe6, 0xed, 0xa3, 0x5c, 0xa4, 0x04, 0x0b,
+               0xb6, 0x65, 0x12, 0x84, 0xbf, 0x07, 0x47, 0xee
+       };
+
+       unsigned char ikmS[] = {
+               0x87, 0x4b, 0xaa, 0x0d, 0xcf, 0x93, 0x59, 0x5a,
+               0x24, 0xa4, 0x5a, 0x7f, 0x04, 0x2e, 0x0d, 0x22,
+               0xd3, 0x68, 0x74, 0x7d, 0xaa, 0xa7, 0xe1, 0x9f,
+               0x80, 0xa8, 0x02, 0xaf, 0x19, 0x20, 0x4b, 0xa8
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x22, 0x24, 0xf3, 0xea, 0x80, 0x0f, 0x7e, 0xc5, 0x5c,
+               0x03, 0xf2, 0x9f, 0xc9, 0x86, 0x5f, 0x6e, 0xe2, 0x70, 0x04,
+               0xf8, 0x18, 0xfc, 0xbd, 0xc6, 0xdc, 0x68, 0x93, 0x2c, 0x1e,
+               0x52, 0xe1, 0x5b, 0x79, 0xe2, 0x64, 0xa9, 0x8f, 0x2c, 0x53,
+               0x5e, 0xf0, 0x67, 0x45, 0xf3, 0xd3, 0x08, 0x62, 0x44, 0x14,
+               0x15, 0x3b, 0x22, 0xc7, 0x33, 0x2b, 0xc1, 0xe6, 0x91, 0xcb,
+               0x4a, 0xf4, 0xd5, 0x34, 0x54
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a34(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0xb9, 0xf3, 0x6d, 0x58, 0xd9, 0xeb, 0x10, 0x16, 0x29,
+               0xa3, 0xe5, 0xa7, 0xb6, 0x3d, 0x2e, 0xe4, 0xaf, 0x42,
+               0xb3, 0x64, 0x42, 0x09, 0xab, 0x37, 0xe0, 0xa2, 0x72,
+               0xd4, 0x43, 0x65, 0x40, 0x7d, 0xb8, 0xe6, 0x55, 0xc7,
+               0x2e, 0x4f, 0xa4, 0x6f, 0x4f, 0xf8, 0x1b, 0x92, 0x46
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x51, 0x78, 0x8c, 0x4e, 0x5d, 0x56, 0x27, 0x67, 0x71,
+               0x03, 0x27, 0x49, 0xd0, 0x15, 0xd3, 0xee, 0xa6, 0x51,
+               0xaf, 0x0c, 0x7b, 0xb8, 0xe3, 0xda, 0x66, 0x9e, 0xff,
+               0xff, 0xed, 0x29, 0x9e, 0xa1, 0xf6, 0x41, 0xdf, 0x62,
+               0x1a, 0xf6, 0x55, 0x79, 0xc1, 0x0f, 0xc0, 0x97, 0x36
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x3b, 0x5a, 0x2b, 0xe0, 0x02, 0xe7, 0xb2, 0x99, 0x27,
+               0xf0, 0x64, 0x42, 0x94, 0x7e, 0x1c, 0xf7, 0x09, 0xb9,
+               0xf8, 0x50, 0x8b, 0x03, 0x82, 0x31, 0x27, 0x38, 0x72,
+               0x23, 0xd7, 0x12, 0x70, 0x34, 0x71, 0xc2, 0x66, 0xef,
+               0xc3, 0x55, 0xf1, 0xbc, 0x20, 0x36, 0xf3, 0x02, 0x7c
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x59, 0x5c, 0xe0, 0xef, 0xf4, 0x05, 0xd4, 0xb3,
+               0xbb, 0x1d, 0x08, 0x30, 0x8d, 0x70, 0xa4, 0xe7,
+               0x72, 0x26, 0xce, 0x11, 0x76, 0x6e, 0x0a, 0x94,
+               0xc4, 0xfd, 0xb5, 0xd9, 0x00, 0x25, 0xc9, 0x78
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x11, 0x04, 0x72, 0xee, 0x0a, 0xe3, 0x28, 0xf5,
+               0x7e, 0xf7, 0x33, 0x2a, 0x98, 0x86, 0xa1, 0x99,
+               0x2d, 0x2c, 0x45, 0xb9, 0xb8, 0xd5, 0xab, 0xc9,
+               0x42, 0x4f, 0xf6, 0x86, 0x30, 0xf7, 0xd3, 0x8d
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x18, 0xee, 0x4d, 0x00, 0x1a, 0x9d, 0x83, 0xa4,
+               0xc6, 0x7e, 0x76, 0xf8, 0x8d, 0xd7, 0x47, 0x76,
+               0x65, 0x76, 0xca, 0xc4, 0x38, 0x72, 0x3b, 0xad,
+               0x07, 0x00, 0xa9, 0x10, 0xa4, 0xd7, 0x17, 0xe6
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x3c, 0x1f, 0xce, 0xb4, 0x77, 0xec, 0x95, 0x4c,
+               0x8d, 0x58, 0xef, 0x32, 0x49, 0xe4, 0xbb, 0x4c,
+               0x38, 0x24, 0x1b, 0x59, 0x25, 0xb9, 0x5f, 0x74,
+               0x86, 0xe4, 0xd9, 0xf1, 0xd0, 0xd3, 0x5f, 0xbb
+       };
+
+       unsigned char ikmR[] = {
+               0xab, 0xcc, 0x2d, 0xa5, 0xb3, 0xfa, 0x81, 0xd8,
+               0xaa, 0xbd, 0x91, 0xf7, 0xf8, 0x00, 0xa8, 0xcc,
+               0xf6, 0x0e, 0xc3, 0x7b, 0x1b, 0x58, 0x5a, 0x5d,
+               0x1d, 0x1a, 0xc7, 0x7f, 0x25, 0x8b, 0x6c, 0xca
+       };
+
+       unsigned char ikmS[] = {
+               0x62, 0x62, 0x03, 0x1f, 0x04, 0x0a, 0x9d, 0xb8,
+               0x53, 0xed, 0xd6, 0xf9, 0x1d, 0x22, 0x72, 0x59,
+               0x6e, 0xab, 0xbc, 0x78, 0xa2, 0xed, 0x2b, 0xd6,
+               0x43, 0xf7, 0x70, 0xec, 0xd0, 0xf1, 0x9b, 0x82
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x6a, 0x1d, 0xe3, 0xfc, 0x26, 0xa3, 0xd4, 0x3f, 0x4e,
+               0x4b, 0xa9, 0x7d, 0xbe, 0x24, 0xf7, 0xe9, 0x91, 0x81, 0x13,
+               0x61, 0x29, 0xc4, 0x8f, 0xbe, 0x87, 0x2d, 0x47, 0x43, 0xe2,
+               0xb1, 0x31, 0x35, 0x7e, 0xd4, 0xf2, 0x9a, 0x7b, 0x31, 0x7d,
+               0xc2, 0x25, 0x09, 0xc7, 0xb0, 0x09, 0x91, 0xae, 0x99, 0x0b,
+               0xf6, 0x5f, 0x8b, 0x23, 0x67, 0x00, 0xc8, 0x2a, 0xb7, 0xc1,
+               0x1a, 0x84, 0x51, 0x14, 0x01
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_AES_128_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a51(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x64, 0x69, 0xc4, 0x1c, 0x5c, 0x81, 0xd3, 0xaa, 0x85,
+               0x43, 0x25, 0x31, 0xec, 0xf6, 0x46, 0x0e, 0xc9, 0x45,
+               0xbd, 0xe1, 0xeb, 0x42, 0x8c, 0xb2, 0xfe, 0xdf, 0x7a,
+               0x29, 0xf5, 0xa6, 0x85, 0xb4, 0xcc, 0xb0, 0xd0, 0x57,
+               0xf0, 0x3e, 0xa2, 0x95, 0x2a, 0x27, 0xbb, 0x45, 0x8b
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xf1, 0x56, 0x41, 0x99, 0xf7, 0xe0, 0xe1, 0x10, 0xec,
+               0x9c, 0x1b, 0xcd, 0xde, 0x33, 0x21, 0x77, 0xfc, 0x35,
+               0xc1, 0xad, 0xf6, 0xe5, 0x7f, 0x8d, 0x1d, 0xf2, 0x40,
+               0x22, 0x22, 0x7f, 0xfa, 0x87, 0x16, 0x86, 0x2d, 0xbd,
+               0xa2, 0xb1, 0xdc, 0x54, 0x6c, 0x9d, 0x11, 0x43, 0x74
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x39, 0xde, 0x89, 0x72, 0x8b, 0xcb, 0x77, 0x42, 0x69,
+               0xf8, 0x82, 0xaf, 0x8d, 0xc5, 0x36, 0x9e, 0x4f, 0x3d,
+               0x63, 0x22, 0xd9, 0x86, 0xe8, 0x72, 0xb3, 0xa8, 0xd0,
+               0x74, 0xc7, 0xc1, 0x8e, 0x85, 0x49, 0xff, 0x3f, 0x85,
+               0xb6, 0xd6, 0x59, 0x2f, 0xf8, 0x7c, 0x3f, 0x31, 0x0c
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x9b, 0x13, 0xc5, 0x10, 0x41, 0x6a, 0xc9, 0x77,
+               0xb5, 0x53, 0xbf, 0x17, 0x41, 0x01, 0x88, 0x09,
+               0xc2, 0x46, 0xa6, 0x95, 0xf4, 0x5e, 0xff, 0x6d,
+               0x3b, 0x03, 0x56, 0xdb, 0xef, 0xe1, 0xe6, 0x60
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x6c, 0x8b, 0x7b, 0xe3, 0xa2, 0x0a, 0x56, 0x84,
+               0xed, 0xec, 0xb4, 0x25, 0x36, 0x19, 0xd9, 0x05,
+               0x1c, 0xe8, 0x58, 0x3b, 0xaf, 0x85, 0x0e, 0x0c,
+               0xb5, 0x3c, 0x40, 0x2b, 0xdc, 0xaf, 0x8e, 0xbb
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x47, 0x7a, 0x50, 0xd8, 0x04, 0xc7, 0xc5, 0x19,
+               0x41, 0xf6, 0x9b, 0x8e, 0x32, 0xfe, 0x82, 0x88,
+               0x38, 0x6e, 0xe1, 0xa8, 0x49, 0x05, 0xfe, 0x49,
+               0x38, 0xd5, 0x89, 0x72, 0xf2, 0x4a, 0xc9, 0x38
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0xf1, 0xf1, 0xa3, 0xbc, 0x95, 0x41, 0x68, 0x71,
+               0x53, 0x9e, 0xcb, 0x51, 0xc3, 0xa8, 0xf0, 0xcf,
+               0x60, 0x8a, 0xfb, 0x40, 0xfb, 0xbe, 0x30, 0x5c,
+               0x0a, 0x72, 0x81, 0x9d, 0x35, 0xc3, 0x3f, 0x1f
+       };
+
+       unsigned char ikmR[] = {
+               0x61, 0x09, 0x2f, 0x3f, 0x56, 0x99, 0x4d, 0xd4,
+               0x24, 0x40, 0x58, 0x99, 0x15, 0x4a, 0x99, 0x18,
+               0x35, 0x3e, 0x3e, 0x00, 0x81, 0x71, 0x51, 0x7a,
+               0xd5, 0x76, 0xb9, 0x00, 0xdd, 0xb2, 0x75, 0xe7
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0xc0, 0x78, 0x36, 0xa0, 0x20, 0x6e, 0x04, 0xe3, 0x1d,
+               0x8a, 0xe9, 0x9b, 0xfd, 0x54, 0x93, 0x80, 0xb0, 0x72, 0xa1,
+               0xb1, 0xb8, 0x2e, 0x56, 0x3c, 0x93, 0x5c, 0x09, 0x58, 0x27,
+               0x82, 0x4f, 0xc1, 0x55, 0x9e, 0xac, 0x6f, 0xb9, 0xe3, 0xc7,
+               0x0c, 0xd3, 0x19, 0x39, 0x68, 0x99, 0x4e, 0x7f, 0xe9, 0x78,
+               0x1a, 0xa1, 0x03, 0xf5, 0xb5, 0x0e, 0x93, 0x4b, 0x5b, 0x2f,
+               0x38, 0x7e, 0x38, 0x12, 0x91
+       };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a52(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x21, 0x43, 0x3e, 0xaf, 0xf2, 0x4d, 0x77, 0x06, 0xf3,
+               0xed, 0x5b, 0x9b, 0x2e, 0x70, 0x9b, 0x07, 0x23, 0x0e,
+               0x2b, 0x11, 0xdf, 0x1f, 0x2b, 0x1f, 0xe0, 0x7b, 0x3c,
+               0x70, 0xd5, 0x94, 0x8a, 0x53, 0xd6, 0xfa, 0x5c, 0x8b,
+               0xed, 0x19, 0x40, 0x20, 0xbd, 0x9d, 0xf0, 0x87, 0x7b
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xc7, 0x4a, 0x76, 0x4b, 0x48, 0x92, 0x07, 0x2e, 0xa8,
+               0xc2, 0xc5, 0x6b, 0x9b, 0xcd, 0x46, 0xc7, 0xf1, 0xe9,
+               0xca, 0x8c, 0xb0, 0xa2, 0x63, 0xf8, 0xb4, 0x0c, 0x2b,
+               0xa5, 0x9a, 0xc9, 0xc8, 0x57, 0x03, 0x3f, 0x17, 0x60,
+               0x19, 0x56, 0x22, 0x18, 0x76, 0x9d, 0x3e, 0x04, 0x52
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0xdc, 0x8c, 0xd6, 0x88, 0x63, 0x47, 0x4d, 0x6e, 0x9c,
+               0xbb, 0x6a, 0x65, 0x93, 0x35, 0xa8, 0x6a, 0x54, 0xe0,
+               0x36, 0x24, 0x9d, 0x41, 0xac, 0xf9, 0x09, 0xe7, 0x38,
+               0xc8, 0x47, 0xff, 0x2b, 0xd3, 0x6f, 0xe3, 0xfc, 0xac,
+               0xda, 0x4e, 0xde, 0xda, 0x70, 0x32, 0xc0, 0xa2, 0x20
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x53, 0x0b, 0xbc, 0x2f, 0x68, 0xf0, 0x78, 0xdc,
+               0xcc, 0x89, 0xcc, 0x37, 0x1b, 0x4f, 0x4a, 0xde,
+               0x37, 0x2c, 0x94, 0x72, 0xba, 0xfe, 0x46, 0x01,
+               0xa8, 0x43, 0x2c, 0xbb, 0x93, 0x4f, 0x52, 0x8d
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x6e, 0x25, 0x07, 0x5d, 0xdc, 0xc5, 0x28, 0xc9,
+               0x0e, 0xf9, 0x21, 0x8f, 0x80, 0x0c, 0xa3, 0xdf,
+               0xe1, 0xb8, 0xff, 0x40, 0x42, 0xde, 0x50, 0x33,
+               0x13, 0x3a, 0xdb, 0x8b, 0xd5, 0x4c, 0x40, 0x1d
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x6f, 0x6f, 0xbd, 0x0d, 0x1c, 0x77, 0x33, 0xf7,
+               0x96, 0x46, 0x1b, 0x32, 0x35, 0xa8, 0x56, 0xcc,
+               0x34, 0xf6, 0x76, 0xfe, 0x61, 0xed, 0x50, 0x9d,
+               0xfc, 0x18, 0xfa, 0x16, 0xef, 0xe6, 0xbe, 0x78
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0xe1, 0xa4, 0xe1, 0xd5, 0x0c, 0x4b, 0xfc, 0xf8,
+               0x90, 0xf2, 0xb4, 0xc7, 0xd6, 0xb2, 0xd2, 0xac,
+               0xa6, 0x13, 0x68, 0xed, 0xdc, 0x3c, 0x84, 0x16,
+               0x2d, 0xf2, 0x85, 0x68, 0x43, 0xe1, 0x05, 0x7a
+       };
+
+       unsigned char ikmR[] = {
+               0xee, 0x51, 0xde, 0xc3, 0x04, 0xab, 0xf9, 0x93,
+               0xef, 0x8f, 0xd5, 0x2a, 0xac, 0xdd, 0x3b, 0x53,
+               0x91, 0x08, 0xbb, 0xf6, 0xe4, 0x91, 0x94, 0x32,
+               0x66, 0xc1, 0xde, 0x89, 0xec, 0x59, 0x6a, 0x17
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0xf3, 0x36, 0x57, 0x8b, 0x72, 0xad, 0x79, 0x32, 0xfe,
+               0x86, 0x7c, 0xc4, 0xd2, 0xd4, 0x4a, 0x71, 0x8a, 0x31, 0x80,
+               0x37, 0xa0, 0xec, 0x27, 0x11, 0x63, 0x69, 0x9c, 0xee, 0x65,
+               0x3f, 0xa8, 0x05, 0xc1, 0xfe, 0xc9, 0x55, 0xe5, 0x62, 0x66,
+               0x3e, 0x0c, 0x20, 0x61, 0xbb, 0x96, 0xa8, 0x7d, 0x78, 0x89,
+               0x2b, 0xff, 0x0c, 0xc0, 0xba, 0xd7, 0x90, 0x6c, 0x2d, 0x99,
+               0x8e, 0xbe, 0x1a, 0x72, 0x46
+       };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a53(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x25, 0x88, 0x1f, 0x21, 0x99, 0x35, 0xee, 0xc5, 0xba,
+               0x70, 0xd7, 0xb4, 0x21, 0xf1, 0x3c, 0x35, 0x00, 0x57,
+               0x34, 0xf3, 0xe4, 0xd9, 0x59, 0x68, 0x02, 0x70, 0xf5,
+               0x5d, 0x71, 0xe2, 0xf5, 0xcb, 0x3b, 0xd2, 0xda, 0xce,
+               0xd2, 0x77, 0x0b, 0xf3, 0xd9, 0xd4, 0x91, 0x68, 0x72
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x65, 0x3f, 0x00, 0x36, 0xe5, 0x2a, 0x37, 0x6f, 0x5d,
+               0x2d, 0xd8, 0x5b, 0x32, 0x04, 0xb5, 0x54, 0x55, 0xb7,
+               0x83, 0x5c, 0x23, 0x12, 0x55, 0xae, 0x09, 0x8d, 0x09,
+               0xed, 0x13, 0x87, 0x19, 0xb9, 0x71, 0x85, 0x12, 0x97,
+               0x86, 0x33, 0x8a, 0xb6, 0x54, 0x3f, 0x75, 0x31, 0x93
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x60, 0x87, 0x87, 0x06, 0x11, 0x7f, 0x22, 0x18, 0x0c,
+               0x78, 0x8e, 0x62, 0xdf, 0x6a, 0x59, 0x5b, 0xc4, 0x19,
+               0x06, 0x09, 0x6a, 0x11, 0xa9, 0x51, 0x3e, 0x84, 0xf0,
+               0x14, 0x1e, 0x43, 0x23, 0x9e, 0x81, 0xa9, 0x8d, 0x7a,
+               0x23, 0x5a, 0xbc, 0x64, 0x11, 0x2f, 0xcb, 0x8d, 0xdd
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x56, 0xc4, 0xd6, 0xc1, 0xd3, 0xa4, 0x6c, 0x70,
+               0xfd, 0x8f, 0x4e, 0xcd, 0xa5, 0xd2, 0x7c, 0x70,
+               0x88, 0x6e, 0x34, 0x8e, 0xfb, 0x51, 0xbd, 0x5e,
+               0xde, 0xaa, 0x39, 0xff, 0x6c, 0xe3, 0x43, 0x89
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0xd2, 0xd3, 0xe4, 0x8e, 0xd7, 0x68, 0x32, 0xb6,
+               0xb3, 0xf2, 0x8f, 0xa8, 0x4b, 0xe5, 0xf1, 0x1f,
+               0x09, 0x53, 0x3c, 0x0e, 0x3c, 0x71, 0x82, 0x5a,
+               0x34, 0xfb, 0x0f, 0x13, 0x20, 0x89, 0x1b, 0x51
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xeb, 0x0d, 0x31, 0x2b, 0x62, 0x63, 0x99, 0x5b,
+               0x4c, 0x77, 0x61, 0xe6, 0x4b, 0x68, 0x8c, 0x21,
+               0x5f, 0xfd, 0x60, 0x43, 0xff, 0x3b, 0xad, 0x23,
+               0x68, 0xc8, 0x62, 0x78, 0x4c, 0xbe, 0x6e, 0xff
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x0e, 0xcd, 0x21, 0x20, 0x19, 0x00, 0x81, 0x38,
+               0xa3, 0x1f, 0x91, 0x04, 0xd5, 0xdb, 0xa7, 0x6b,
+               0x9f, 0x8e, 0x34, 0xd5, 0xb9, 0x96, 0x04, 0x1f,
+               0xff, 0x9e, 0x3d, 0xf2, 0x21, 0xdd, 0x0d, 0x5d
+       };
+
+       unsigned char ikmR[] = {
+               0xd3, 0x22, 0x36, 0xd8, 0x37, 0x8b, 0x95, 0x63,
+               0x84, 0x06, 0x53, 0x78, 0x9e, 0xb7, 0xbc, 0x33,
+               0xc3, 0xc7, 0x20, 0xe5, 0x37, 0x39, 0x17, 0x27,
+               0xbf, 0x1c, 0x81, 0x2d, 0x0e, 0xac, 0x11, 0x0f
+       };
+
+       unsigned char ikmS[] = {
+               0x0e, 0x6b, 0xe0, 0x85, 0x12, 0x83, 0xf9, 0x32,
+               0x72, 0x95, 0xfd, 0x49, 0x85, 0x8a, 0x8c, 0x89,
+               0x08, 0xea, 0x97, 0x83, 0x21, 0x29, 0x45, 0xee,
+               0xf6, 0xc5, 0x98, 0xee, 0x0a, 0x3c, 0xed, 0xbb
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x0d, 0x51, 0x76, 0xae, 0xdb, 0xa5, 0x5b, 0xc4, 0x17,
+               0x09, 0x26, 0x1e, 0x91, 0x95, 0xc5, 0x14, 0x6b, 0xb6, 0x2d,
+               0x78, 0x30, 0x31, 0x28, 0x07, 0x75, 0xf3, 0x2e, 0x50, 0x7d,
+               0x79, 0xb5, 0xcb, 0xc5, 0x74, 0x8b, 0x6b, 0xe6, 0x35, 0x97,
+               0x60, 0xc7, 0x3c, 0xfe, 0x10, 0xca, 0x19, 0x52, 0x1a, 0xf7,
+               0x04, 0xca, 0x6d, 0x91, 0xff, 0x32, 0xfc, 0x07, 0x39, 0x52,
+               0x7b, 0x93, 0x85, 0xd4, 0x15
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a54(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x9e, 0xad, 0xfa, 0x0f, 0x95, 0x48, 0x35, 0xe7, 0xe9,
+               0x20, 0xff, 0xe5, 0x6d, 0xec, 0x6b, 0x31, 0xa0, 0x46,
+               0x27, 0x1c, 0xf7, 0x1f, 0xdd, 0xa5, 0x5d, 0xb7, 0x29,
+               0x26, 0xe1, 0xd8, 0xfa, 0xe9, 0x4c, 0xc6, 0x28, 0x0f,
+               0xcf, 0xab, 0xd8, 0xdb, 0x71, 0xea, 0xa6, 0x5c, 0x05
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xe3, 0x57, 0xad, 0x10, 0xd7, 0x52, 0x40, 0x22, 0x4d,
+               0x40, 0x95, 0xc9, 0xf6, 0x15, 0x0a, 0x2e, 0xd2, 0x17,
+               0x9c, 0x0f, 0x87, 0x8e, 0x4f, 0x2d, 0xb8, 0xca, 0x95,
+               0xd3, 0x65, 0xd1, 0x74, 0xd0, 0x59, 0xff, 0x8c, 0x3e,
+               0xb3, 0x8e, 0xa9, 0xa6, 0x5c, 0xfc, 0x8e, 0xae, 0xb8
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x2f, 0xa5, 0x6d, 0x00, 0xf8, 0xdd, 0x47, 0x9d, 0x67,
+               0xa2, 0xec, 0x33, 0x08, 0x32, 0x5c, 0xf3, 0xbb, 0xcc,
+               0xaf, 0x10, 0x2a, 0x64, 0xff, 0xcc, 0xdb, 0x00, 0x6b,
+               0xd7, 0xdc, 0xb9, 0x32, 0x68, 0x5b, 0x9a, 0x7b, 0x49,
+               0xcd, 0xc0, 0x94, 0xa8, 0x5f, 0xec, 0x1d, 0xa5, 0xef
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0xc5, 0x2b, 0x45, 0x92, 0xcd, 0x33, 0xdd, 0x38,
+               0xb2, 0xa3, 0x61, 0x31, 0x08, 0xdd, 0xda, 0x28,
+               0xdc, 0xf7, 0xf0, 0x3d, 0x30, 0xf2, 0xa0, 0x97,
+               0x03, 0xf7, 0x58, 0xbf, 0xa8, 0x02, 0x9c, 0x9a
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x2f, 0x03, 0xbe, 0xbc, 0x57, 0x7e, 0x57, 0x29,
+               0xe1, 0x48, 0x55, 0x49, 0x91, 0x78, 0x72, 0x22,
+               0xb5, 0xc2, 0xa0, 0x2b, 0x77, 0xe9, 0xb1, 0xac,
+               0x38, 0x05, 0x41, 0xf7, 0x10, 0xe5, 0xa3, 0x18
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xe0, 0x1d, 0xd4, 0x9e, 0x8b, 0xfc, 0x3d, 0x92,
+               0x16, 0xab, 0xc1, 0xbe, 0x83, 0x2f, 0x04, 0x18,
+               0xad, 0xf8, 0xb4, 0x7a, 0x7b, 0x5a, 0x33, 0x0a,
+               0x74, 0x36, 0xc3, 0x1e, 0x33, 0xd7, 0x65, 0xd7
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0xf3, 0xa0, 0x7f, 0x19, 0x47, 0x03, 0xe3, 0x21,
+               0xef, 0x1f, 0x75, 0x3a, 0x1b, 0x9f, 0xe2, 0x7a,
+               0x49, 0x8d, 0xfd, 0xfa, 0x30, 0x91, 0x51, 0xd7,
+               0x0b, 0xed, 0xd8, 0x96, 0xc2, 0x39, 0xc4, 0x99
+       };
+
+       unsigned char ikmR[] = {
+               0x12, 0x40, 0xe5, 0x5a, 0x0a, 0x03, 0x54, 0x8d,
+               0x7f, 0x96, 0x3e, 0xf7, 0x83, 0xb6, 0xa7, 0x36,
+               0x2c, 0xb5, 0x05, 0xe6, 0xb3, 0x1d, 0xfd, 0x04,
+               0xc8, 0x1d, 0x9b, 0x29, 0x45, 0x43, 0xbf, 0xbd
+       };
+
+       unsigned char ikmS[] = {
+               0xce, 0x2a, 0x03, 0x87, 0xa2, 0xeb, 0x88, 0x70,
+               0xa3, 0xa9, 0x2c, 0x34, 0xa2, 0x97, 0x5f, 0x0f,
+               0x3f, 0x27, 0x1a, 0xf4, 0x38, 0x4d, 0x44, 0x6c,
+               0x7d, 0xc1, 0x52, 0x4a, 0x6c, 0x6c, 0x51, 0x5a
+       };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x35, 0x39, 0x91, 0x7e, 0xe2, 0x6f, 0x8a, 0xe0, 0xaa,
+               0x5f, 0x78, 0x4a, 0x38, 0x79, 0x81, 0xb1, 0x3d, 0xe3, 0x31,
+               0x24, 0xa3, 0xcd, 0xe8, 0x8b, 0x94, 0x67, 0x20, 0x30, 0x18,
+               0x31, 0x10, 0xf3, 0x31, 0x40, 0x01, 0x15, 0x85, 0x58, 0x08,
+               0x24, 0x4f, 0xf0, 0xc5, 0xb6, 0xca, 0x61, 0x04, 0x48, 0x3a,
+               0xc9, 0x57, 0x24, 0x48, 0x1d, 0x41, 0xbd, 0xcd, 0x9f, 0x15,
+               0xb4, 0x30, 0xad, 0x16, 0xf6
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P256,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_CHACHA20_POLY1305,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a61(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x17, 0x0f, 0x8b, 0xed, 0xdf, 0xe9, 0x49, 0xb7, 0x5e,
+               0xf9, 0xc3, 0x87, 0xe2, 0x01, 0xba, 0xf4, 0x13, 0x2f,
+               0xa7, 0x37, 0x45, 0x93, 0xdf, 0xaf, 0xa9, 0x07, 0x68,
+               0x78, 0x8b, 0x7b, 0x2b, 0x20, 0x0a, 0xaf, 0xcc, 0x6d,
+               0x80, 0xea, 0x4c, 0x79, 0x5a, 0x7c, 0x5b, 0x84, 0x1a
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xd9, 0xee, 0x24, 0x8e, 0x22, 0x0c, 0xa2, 0x4a, 0xc0,
+               0x0b, 0xbb, 0xe7, 0xe2, 0x21, 0xa8, 0x32, 0xe4, 0xf7,
+               0xfa, 0x64, 0xc4, 0xfb, 0xab, 0x39, 0x45, 0xb6, 0xf3,
+               0xaf, 0x0c, 0x5e, 0xcd, 0x5e, 0x16, 0x81, 0x5b, 0x32,
+               0x8b, 0xe4, 0x95, 0x4a, 0x05, 0xfd, 0x35, 0x22, 0x56
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x14, 0x2c, 0xf1, 0xe0, 0x2d, 0x1f, 0x58, 0xd9, 0x28,
+               0x5f, 0x2a, 0xf7, 0xdc, 0xfa, 0x44, 0xf7, 0xc3, 0xf2,
+               0xd1, 0x5c, 0x73, 0xd4, 0x60, 0xc4, 0x8c, 0x6e, 0x0e,
+               0x50, 0x6a, 0x31, 0x44, 0xba, 0xe3, 0x52, 0x84, 0xe7,
+               0xe2, 0x21, 0x10, 0x5b, 0x61, 0xd2, 0x4e, 0x1c, 0x7a
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x05, 0xe2, 0xe5, 0xbd, 0x9f, 0x0c, 0x30, 0x83,
+               0x2b, 0x80, 0xa2, 0x79, 0xff, 0x21, 0x1c, 0xc6,
+               0x5e, 0xce, 0xb0, 0xd9, 0x70, 0x01, 0x52, 0x40,
+               0x85, 0xd6, 0x09, 0xea, 0xd6, 0x0d, 0x04, 0x12
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0xfc, 0xa6, 0x97, 0x44, 0xbb, 0x53, 0x7f, 0x5b,
+               0x7a, 0x15, 0x96, 0xdb, 0xf3, 0x4e, 0xaa, 0x8d,
+               0x84, 0xbf, 0x2e, 0x3e, 0xe7, 0xf1, 0xa1, 0x55,
+               0xd4, 0x1b, 0xd3, 0x62, 0x4a, 0xa9, 0x2b, 0x63
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xf3, 0x89, 0xbe, 0xaa, 0xc6, 0xfc, 0xf6, 0xc0,
+               0xd9, 0x37, 0x6e, 0x20, 0xf9, 0x7e, 0x36, 0x4f,
+               0x06, 0x09, 0xa8, 0x8f, 0x1b, 0xc7, 0x6d, 0x73,
+               0x28, 0xe9, 0x10, 0x4d, 0xf8, 0x47, 0x70, 0x13
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = { 0x7f, 0x06, 0xab, 0x82, 0x15, 0x10, 0x5f, 0xc4,
+                                0x6a, 0xce, 0xeb, 0x2e, 0x3d, 0xc5, 0x02, 0x8b,
+                                0x44, 0x36, 0x4f, 0x96, 0x04, 0x26, 0xeb, 0x0d,
+                                0x8e, 0x40, 0x26, 0xc2, 0xf8, 0xb5, 0xd7, 0xe7,
+                                0xa9, 0x86, 0x68, 0x8f, 0x15, 0x91, 0xab, 0xf5,
+                                0xab, 0x75, 0x3c, 0x35, 0x7a, 0x5d, 0x6f, 0x04,
+                                0x40, 0x41, 0x4b, 0x4e, 0xd4, 0xed, 0xe7, 0x13,
+                                0x17, 0x77, 0x2a, 0xc9, 0x8d, 0x92, 0x39, 0xf7,
+                                0x09, 0x04 };
+
+       unsigned char ikmR[] = { 0x2a, 0xd9, 0x54, 0xbb, 0xe3, 0x9b, 0x71, 0x22,
+                                0x52, 0x9f, 0x7d, 0xde, 0x78, 0x0b, 0xff, 0x62,
+                                0x6c, 0xd9, 0x7f, 0x85, 0x0d, 0x07, 0x84, 0xa4,
+                                0x32, 0x78, 0x4e, 0x69, 0xd8, 0x6e, 0xcc, 0xaa,
+                                0xde, 0x43, 0xb6, 0xc1, 0x0a, 0x8f, 0xfd, 0xb9,
+                                0x4b, 0xf9, 0x43, 0xc6, 0xda, 0x47, 0x9d, 0xb1,
+                                0x37, 0x91, 0x4e, 0xc8, 0x35, 0xa7, 0xe7, 0x15,
+                                0xe3, 0x6e, 0x45, 0xe2, 0x9b, 0x58, 0x7b, 0xab,
+                                0x3b, 0xf1 };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x01, 0x38, 0xb3, 0x85, 0xca, 0x16, 0xbb, 0x0d, 0x5f,
+               0xa0, 0xc0, 0x66, 0x5f, 0xbb, 0xd7, 0xe6, 0x9e, 0x3e, 0xe2,
+               0x9f, 0x63, 0x99, 0x1d, 0x3e, 0x9b, 0x5f, 0xa7, 0x40, 0xaa,
+               0xb8, 0x90, 0x0a, 0xae, 0xed, 0x46, 0xed, 0x73, 0xa4, 0x90,
+               0x55, 0x75, 0x84, 0x25, 0xa0, 0xce, 0x36, 0x50, 0x7c, 0x54,
+               0xb2, 0x9c, 0xc5, 0xb8, 0x5a, 0x5c, 0xee, 0x6b, 0xae, 0x0c,
+               0xf1, 0xc2, 0x1f, 0x27, 0x31, 0xec, 0xe2, 0x01, 0x3d, 0xc3,
+               0xfb, 0x7c, 0x8d, 0x21, 0x65, 0x4b, 0xb1, 0x61, 0xb4, 0x63,
+               0x96, 0x2c, 0xa1, 0x9e, 0x8c, 0x65, 0x4f, 0xf2, 0x4c, 0x94,
+               0xdd, 0x28, 0x98, 0xde, 0x12, 0x05, 0x1f, 0x1e, 0xd0, 0x69,
+               0x22, 0x37, 0xfb, 0x02, 0xb2, 0xf8, 0xd1, 0xdc, 0x1c, 0x73,
+               0xe9, 0xb3, 0x66, 0xb5, 0x29, 0xeb, 0x43, 0x6e, 0x98, 0xa9,
+               0x96, 0xee, 0x52, 0x2a, 0xef, 0x86, 0x3d, 0xd5, 0x73, 0x9d,
+               0x2f, 0x29, 0xb0
+       };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P521,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA512,
+               .aead = GNUTLS_HPKE_AEAD_AES_256_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a62(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0xde, 0x69, 0xe9, 0xd9, 0x43, 0xa5, 0xd0, 0xb7, 0x0b,
+               0xe3, 0x35, 0x9a, 0x19, 0xf3, 0x17, 0xbd, 0x9a, 0xca,
+               0x4a, 0x2e, 0xbb, 0x43, 0x32, 0xa3, 0x9b, 0xcd, 0xfc,
+               0x97, 0xd5, 0xfe, 0x62, 0xf3, 0xa7, 0x77, 0x02, 0xf4,
+               0x82, 0x2c, 0x3b, 0xe5, 0x31, 0xaa, 0x78, 0x43, 0xa1
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x77, 0xa1, 0x61, 0x62, 0x83, 0x1f, 0x90, 0xde, 0x35,
+               0x0f, 0xea, 0x91, 0x52, 0xcf, 0xc6, 0x85, 0xec, 0xfa,
+               0x10, 0xac, 0xb4, 0xf7, 0x99, 0x4f, 0x41, 0xae, 0xd4,
+               0x3f, 0xa5, 0x43, 0x1f, 0x23, 0x82, 0xd0, 0x78, 0xec,
+               0x88, 0xba, 0xec, 0x53, 0x94, 0x39, 0x84, 0x55, 0x3e
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0xf1, 0xd4, 0x8d, 0x09, 0xf1, 0x26, 0xb9, 0x00, 0x3b,
+               0x4c, 0x7d, 0x3f, 0xe6, 0x77, 0x9c, 0x7c, 0x92, 0x17,
+               0x31, 0x88, 0xa2, 0xbb, 0x74, 0x65, 0xba, 0x43, 0xd8,
+               0x99, 0xa6, 0x39, 0x8a, 0x33, 0x39, 0x14, 0xd2, 0xbb,
+               0x19, 0xfd, 0x76, 0x9d, 0x53, 0xf3, 0xec, 0x73, 0x36
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x62, 0x69, 0x1f, 0x0f, 0x97, 0x1e, 0x34, 0xde,
+               0x38, 0x37, 0x0b, 0xff, 0x24, 0xde, 0xb5, 0xa7,
+               0xd4, 0x0a, 0xb6, 0x28, 0x09, 0x3d, 0x30, 0x4b,
+               0xe6, 0x09, 0x46, 0xaf, 0xcd, 0xb3, 0xa9, 0x36
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x76, 0x08, 0x3c, 0x6d, 0x1b, 0x68, 0x09, 0xda,
+               0x08, 0x85, 0x84, 0x67, 0x43, 0x27, 0xb3, 0x94,
+               0x88, 0xea, 0xf6, 0x65, 0xf0, 0x73, 0x11, 0x51,
+               0x12, 0x84, 0x52, 0xe0, 0x4c, 0xe8, 0x1b, 0xff
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x0c, 0x7c, 0xfc, 0x09, 0x76, 0xe2, 0x5a, 0xe7,
+               0x68, 0x0c, 0xf9, 0x09, 0xae, 0x2d, 0xe1, 0x85,
+               0x9c, 0xd9, 0xb6, 0x79, 0x61, 0x0a, 0x14, 0xbe,
+               0xc4, 0x0d, 0x69, 0xb9, 0x17, 0x85, 0xb2, 0xf6
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = { 0xf3, 0xeb, 0xfa, 0x9a, 0x69, 0xa9, 0x24, 0xe6,
+                                0x72, 0x11, 0x4f, 0xcd, 0x9e, 0x06, 0xfa, 0x95,
+                                0x59, 0xe9, 0x37, 0xf7, 0xec, 0xcc, 0xe4, 0x18,
+                                0x1a, 0x2b, 0x50, 0x6d, 0xf5, 0x3d, 0xbe, 0x51,
+                                0x4b, 0xe1, 0x2f, 0x09, 0x4b, 0xb2, 0x8e, 0x01,
+                                0xde, 0x19, 0xdd, 0x34, 0x5b, 0x4f, 0x7e, 0xde,
+                                0x5a, 0xd7, 0xea, 0xa6, 0xb9, 0xc3, 0x01, 0x95,
+                                0x92, 0xec, 0x68, 0xea, 0xae, 0x9a, 0x14, 0x73,
+                                0x2c, 0xe0 };
+
+       unsigned char ikmR[] = { 0xa2, 0xa2, 0x45, 0x87, 0x05, 0xe2, 0x78, 0xe5,
+                                0x74, 0xf8, 0x35, 0xef, 0xfe, 0xcd, 0x18, 0x23,
+                                0x2f, 0x8a, 0x4c, 0x45, 0x9e, 0x75, 0x50, 0xa0,
+                                0x9d, 0x44, 0x34, 0x8a, 0xe5, 0xd3, 0xb1, 0xea,
+                                0x9d, 0x95, 0xc5, 0x19, 0x95, 0xe6, 0x57, 0xad,
+                                0x6f, 0x7c, 0xae, 0x65, 0x9f, 0x5e, 0x18, 0x61,
+                                0x26, 0xa4, 0x71, 0xc0, 0x17, 0xf8, 0xf5, 0xe4,
+                                0x1d, 0xa9, 0xeb, 0xa7, 0x4d, 0x4e, 0x04, 0x73,
+                                0xe1, 0x79 };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x00, 0x85, 0xef, 0xf0, 0x83, 0x5c, 0xc8, 0x43, 0x51,
+               0xf3, 0x24, 0x71, 0xd3, 0x2a, 0xa4, 0x53, 0xcd, 0xc1, 0xf6,
+               0x41, 0x8e, 0xaa, 0xec, 0xf1, 0xc2, 0x82, 0x42, 0x10, 0xeb,
+               0x1d, 0x48, 0xd0, 0x76, 0x8b, 0x36, 0x81, 0x10, 0xfa, 0xb2,
+               0x14, 0x07, 0xc3, 0x24, 0xb8, 0xbb, 0x4b, 0xec, 0x63, 0xf0,
+               0x42, 0xcf, 0xa4, 0xd0, 0x86, 0x8d, 0x19, 0xb7, 0x60, 0xeb,
+               0x4b, 0xeb, 0xa1, 0xbf, 0xf7, 0x93, 0xb3, 0x00, 0x36, 0xd2,
+               0xc6, 0x14, 0xd5, 0x57, 0x30, 0xbd, 0x2a, 0x40, 0xc7, 0x18,
+               0xf9, 0x46, 0x6f, 0xaf, 0x4d, 0x5f, 0x81, 0x70, 0xd2, 0x2b,
+               0x6d, 0xf9, 0x8d, 0xfe, 0x0c, 0x06, 0x7d, 0x02, 0xb3, 0x49,
+               0xae, 0x4a, 0x14, 0x2e, 0x0c, 0x03, 0x41, 0x8f, 0x0a, 0x14,
+               0x79, 0xff, 0x78, 0xa3, 0xdb, 0x07, 0xae, 0x2c, 0x2e, 0x89,
+               0xe5, 0x84, 0x0f, 0x71, 0x2c, 0x17, 0x4b, 0xa2, 0x11, 0x8e,
+               0x90, 0xfd, 0xcb
+       };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P521,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA512,
+               .aead = GNUTLS_HPKE_AEAD_AES_256_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a63(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x01, 0x16, 0xae, 0xb3, 0xa1, 0xc4, 0x05, 0xc6, 0x1b,
+               0x1c, 0xe4, 0x76, 0x00, 0xb7, 0xec, 0xd1, 0x1d, 0x89,
+               0xb9, 0xc0, 0x8c, 0x40, 0x8b, 0x7e, 0x2d, 0x1e, 0x00,
+               0xa4, 0xd6, 0x46, 0x96, 0xd1, 0x2e, 0x68, 0x81, 0xdc,
+               0x61, 0x68, 0x82, 0x09, 0xa8, 0x20, 0x74, 0x27, 0xf9
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0x37, 0xec, 0xe0, 0xcf, 0x67, 0x41, 0xf4, 0x43, 0xe9,
+               0xd7, 0x3b, 0x99, 0x66, 0xdc, 0x0b, 0x22, 0x84, 0x99,
+               0xbb, 0x21, 0xfb, 0xf3, 0x13, 0x94, 0x83, 0x27, 0x23,
+               0x1e, 0x70, 0xa1, 0x83, 0x80, 0xe0, 0x80, 0x52, 0x9c,
+               0x02, 0x67, 0xf3, 0x99, 0xba, 0x7c, 0x53, 0x9c, 0xc6
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0xd1, 0x7b, 0x04, 0x5c, 0xac, 0x96, 0x3e, 0x45, 0xd5,
+               0x5f, 0xd3, 0x69, 0x2e, 0xc1, 0x7f, 0x10, 0x0d, 0xf6,
+               0x6a, 0xc0, 0x6d, 0x91, 0xf3, 0xb6, 0xaf, 0x8e, 0xfa,
+               0x7e, 0xd3, 0xc8, 0x89, 0x55, 0x50, 0xeb, 0x75, 0x3b,
+               0xc8, 0x01, 0xfe, 0x4b, 0xd2, 0x70, 0x05, 0xb4, 0xbd
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0x8d, 0x78, 0x74, 0x8d, 0x63, 0x2f, 0x95, 0xb8,
+               0xce, 0x0c, 0x67, 0xd7, 0x0f, 0x4a, 0xd1, 0x75,
+               0x7e, 0x61, 0xe8, 0x72, 0xb5, 0x94, 0x1e, 0x14,
+               0x69, 0x86, 0x80, 0x4b, 0x39, 0x90, 0x15, 0x4b
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x80, 0xa4, 0x75, 0x32, 0x30, 0x90, 0x0e, 0xa7,
+               0x85, 0xb6, 0xc8, 0x07, 0x75, 0x09, 0x28, 0x01,
+               0xfe, 0x91, 0x18, 0x37, 0x46, 0x47, 0x9f, 0x9b,
+               0x04, 0xc3, 0x05, 0xe1, 0xdb, 0x9d, 0x1f, 0x4d
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x62, 0x0b, 0x17, 0x6d, 0x73, 0x7c, 0xf3, 0x66,
+               0xbc, 0xc2, 0x0d, 0x96, 0xad, 0xb5, 0x4e, 0xc1,
+               0x56, 0x97, 0x82, 0x20, 0x87, 0x9b, 0x67, 0x92,
+               0x36, 0x89, 0xe6, 0xdc, 0xa3, 0x62, 0x10, 0xed
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = { 0xfe, 0x1c, 0x58, 0x9c, 0x2a, 0x05, 0x89, 0x38,
+                                0x95, 0xa5, 0x37, 0xf3, 0x8c, 0x7c, 0xb4, 0x30,
+                                0x0b, 0x5a, 0x7e, 0x8f, 0xef, 0x3d, 0x6c, 0xcb,
+                                0x8f, 0x07, 0xa4, 0x98, 0x02, 0x9c, 0x61, 0xe9,
+                                0x02, 0x62, 0xe0, 0x09, 0xdc, 0x25, 0x4c, 0x7f,
+                                0x62, 0x35, 0xf9, 0xc6, 0xb2, 0xfd, 0x6a, 0xef,
+                                0xf0, 0xa7, 0x14, 0xdb, 0x13, 0x1b, 0x09, 0x25,
+                                0x8c, 0x16, 0xe2, 0x17, 0xb7, 0xbd, 0x2a, 0xa6,
+                                0x19, 0xb0 };
+
+       unsigned char ikmR[] = { 0x8f, 0xee, 0xa0, 0x43, 0x84, 0x81, 0xfc, 0x0e,
+                                0xcd, 0x47, 0x0d, 0x6a, 0xdf, 0xcd, 0xa3, 0x34,
+                                0xa7, 0x59, 0xc6, 0xb8, 0x65, 0x04, 0x52, 0xc5,
+                                0xa5, 0xdd, 0x9b, 0x2d, 0xd2, 0xcc, 0x9b, 0xe3,
+                                0x3d, 0x2b, 0xb7, 0xee, 0x64, 0x60, 0x5f, 0xc0,
+                                0x7a, 0xb4, 0x66, 0x4a, 0x58, 0xbb, 0x9a, 0x8d,
+                                0xe8, 0x0d, 0xef, 0xe5, 0x10, 0xb6, 0xc9, 0x7d,
+                                0x2d, 0xaf, 0x85, 0xb9, 0x2c, 0xd4, 0xbb, 0x0a,
+                                0x66, 0xbf };
+
+       unsigned char ikmS[] = { 0x2f, 0x66, 0xa6, 0x8b, 0x85, 0xef, 0x04, 0x82,
+                                0x2b, 0x05, 0x4e, 0xf5, 0x21, 0x83, 0x8c, 0x00,
+                                0xc6, 0x4f, 0x8b, 0x62, 0x26, 0x93, 0x55, 0x93,
+                                0xb6, 0x9e, 0x13, 0xa1, 0xa2, 0x46, 0x1a, 0x4f,
+                                0x1a, 0x74, 0xc1, 0x0c, 0x83, 0x6e, 0x87, 0xee,
+                                0xd1, 0x50, 0xc0, 0xdb, 0x85, 0xd4, 0xe4, 0xf5,
+                                0x06, 0xcb, 0xb7, 0x46, 0x14, 0x9b, 0xef, 0xac,
+                                0x6f, 0x5c, 0x07, 0xdc, 0x48, 0xa6, 0x15, 0xef,
+                                0x92, 0xdb };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x01, 0x7d, 0xe1, 0x2e, 0xde, 0x7f, 0x72, 0xcb, 0x10,
+               0x1d, 0xab, 0x36, 0xa1, 0x11, 0x26, 0x5c, 0x97, 0xb3, 0x65,
+               0x48, 0x16, 0xdc, 0xd6, 0x18, 0x3f, 0x80, 0x9d, 0x4b, 0x3d,
+               0x11, 0x1f, 0xe7, 0x59, 0x49, 0x7f, 0x8a, 0xef, 0xdc, 0x5d,
+               0xbb, 0x40, 0xd3, 0xe6, 0xd2, 0x1d, 0xb1, 0x5b, 0xdc, 0x60,
+               0xf1, 0x5f, 0x2a, 0x42, 0x07, 0x61, 0xbc, 0xae, 0xef, 0x73,
+               0xb8, 0x91, 0xc2, 0xb1, 0x17, 0xe9, 0xcf, 0x01, 0xe2, 0x93,
+               0x20, 0xb7, 0x99, 0xbb, 0xc8, 0x6a, 0xfd, 0xc5, 0xea, 0x97,
+               0xd9, 0x41, 0xea, 0x1c, 0x5b, 0xd5, 0xeb, 0xee, 0xac, 0x7a,
+               0x78, 0x4b, 0x3b, 0xab, 0x52, 0x47, 0x46, 0xf3, 0xe6, 0x40,
+               0xec, 0x26, 0xee, 0x1b, 0xd9, 0x12, 0x55, 0xf9, 0x33, 0x0d,
+               0x97, 0x4f, 0x84, 0x50, 0x84, 0x63, 0x7e, 0xe0, 0xe6, 0xfe,
+               0x9f, 0x50, 0x5c, 0x5b, 0x87, 0xc8, 0x6a, 0x4e, 0x1a, 0x6c,
+               0x30, 0x96, 0xdd
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P521,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA512,
+               .aead = GNUTLS_HPKE_AEAD_AES_256_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a64(void)
+{
+       unsigned char seq0_ciphertext[] = {
+               0x94, 0x2a, 0x2a, 0x92, 0xe0, 0x81, 0x7c, 0xf0, 0x32,
+               0xce, 0x61, 0xab, 0xcc, 0xf4, 0xf3, 0xa7, 0xc5, 0xd2,
+               0x1b, 0x79, 0x4e, 0xd9, 0x43, 0x22, 0x7e, 0x07, 0xb7,
+               0xdf, 0x2d, 0x6d, 0xd9, 0x2c, 0x9b, 0x8a, 0x93, 0x71,
+               0x94, 0x9e, 0x65, 0xcc, 0xa2, 0x62, 0x44, 0x8a, 0xb7
+       };
+
+       unsigned char seq1_ciphertext[] = {
+               0xc0, 0xa8, 0x3b, 0x5e, 0xc3, 0xd7, 0x93, 0x3a, 0x09,
+               0x0f, 0x68, 0x17, 0x17, 0x29, 0x03, 0x37, 0xb4, 0xfe,
+               0xde, 0x5b, 0xfa, 0xa0, 0xa4, 0x0e, 0xc2, 0x9f, 0x93,
+               0xac, 0xad, 0x74, 0x28, 0x88, 0xa1, 0x51, 0x3c, 0x64,
+               0x91, 0x04, 0xc3, 0x91, 0xc7, 0x8d, 0x1d, 0x7f, 0x29
+       };
+
+       unsigned char seq2_ciphertext[] = {
+               0x28, 0x47, 0xb2, 0xe0, 0xce, 0x0b, 0x9d, 0xa8, 0xfc,
+               0xa7, 0xb0, 0xe8, 0x1f, 0xf3, 0x89, 0xd1, 0x68, 0x2e,
+               0xe1, 0xb3, 0x88, 0xed, 0x09, 0x57, 0x9b, 0x14, 0x50,
+               0x58, 0xb5, 0xaf, 0x6a, 0x93, 0xa8, 0x5d, 0xd5, 0x0d,
+               0x9f, 0x41, 0x7d, 0xc8, 0x8f, 0x2c, 0x78, 0x53, 0x12
+       };
+
+       hpke_test_encryption_parameters_st enc_params[] = {
+               { .sequence_number = 0,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq0_aad, sizeof(seq0_aad) },
+                 .expected_ciphertext = { seq0_ciphertext,
+                                          sizeof(seq0_ciphertext) } },
+               { .sequence_number = 1,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq1_aad, sizeof(seq1_aad) },
+                 .expected_ciphertext = { seq1_ciphertext,
+                                          sizeof(seq1_ciphertext) } },
+               { .sequence_number = 2,
+                 .plaintext = { plaintext, sizeof(plaintext) },
+                 .aad = { seq2_aad, sizeof(seq2_aad) },
+                 .expected_ciphertext = { seq2_ciphertext,
+                                          sizeof(seq2_ciphertext) } }
+       };
+
+       unsigned char expected_exporter_value1[] = {
+               0xa3, 0x95, 0x02, 0xef, 0x5c, 0xa1, 0x16, 0xaa,
+               0x13, 0x17, 0xbd, 0x95, 0x83, 0xdd, 0x52, 0xf1,
+               0x5b, 0x05, 0x02, 0xb7, 0x1d, 0x90, 0x0f, 0xc8,
+               0xa6, 0x22, 0xd1, 0x96, 0x23, 0xd0, 0xcb, 0x5d
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x74, 0x9e, 0xda, 0x11, 0x2c, 0x4c, 0xfd, 0xd6,
+               0x67, 0x1d, 0x84, 0x59, 0x5f, 0x12, 0xcd, 0x13,
+               0x19, 0x8f, 0xc3, 0xef, 0x93, 0xed, 0x72, 0x36,
+               0x91, 0x78, 0xf3, 0x44, 0xfe, 0x6e, 0x09, 0xc3
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xf8, 0xb4, 0xe7, 0x2c, 0xef, 0xbf, 0xf4, 0xca,
+               0x6c, 0x4e, 0xab, 0xb8, 0xc0, 0x38, 0x32, 0x87,
+               0x08, 0x2c, 0xfc, 0xbb, 0x95, 0x3d, 0x90, 0x0a,
+               0xed, 0x49, 0x59, 0xaf, 0xd0, 0x01, 0x70, 0x95
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = { 0x54, 0x27, 0x27, 0x97, 0xb1, 0xfb, 0xc1, 0x28,
+                                0xa6, 0x96, 0x7f, 0xf1, 0xfd, 0x60, 0x6e, 0x0c,
+                                0x67, 0x86, 0x8f, 0x77, 0x62, 0xce, 0x14, 0x21,
+                                0x43, 0x9c, 0xbc, 0x9e, 0x90, 0xce, 0x1b, 0x28,
+                                0xd5, 0x66, 0xe6, 0xc2, 0xac, 0xbc, 0xe7, 0x12,
+                                0xe4, 0x8e, 0xeb, 0xf2, 0x36, 0x69, 0x6e, 0xb6,
+                                0x80, 0x84, 0x9d, 0x68, 0x73, 0xe9, 0x95, 0x93,
+                                0x95, 0xb2, 0x93, 0x19, 0x75, 0xd6, 0x1d, 0x38,
+                                0xbd, 0x6c };
+
+       unsigned char ikmR[] = { 0x3d, 0xb4, 0x34, 0xa8, 0xbc, 0x25, 0xb2, 0x7e,
+                                0xb0, 0xc5, 0x90, 0xdc, 0x64, 0x99, 0x7a, 0xb1,
+                                0x37, 0x8a, 0x99, 0xf5, 0x2b, 0x2c, 0xb5, 0xa5,
+                                0xa5, 0xb2, 0xfa, 0x54, 0x08, 0x88, 0xf6, 0xc0,
+                                0xf0, 0x97, 0x94, 0xc6, 0x54, 0xf4, 0x46, 0x85,
+                                0x24, 0xe0, 0x40, 0xe6, 0xb4, 0xec, 0xa2, 0xc9,
+                                0xdc, 0xf2, 0x29, 0xf9, 0x08, 0xb9, 0xd3, 0x18,
+                                0xf9, 0x60, 0xcc, 0x9e, 0x9b, 0xaa, 0x92, 0xc5,
+                                0xee, 0xe6 };
+
+       unsigned char ikmS[] = { 0x65, 0xd5, 0x23, 0xd9, 0xb3, 0x7e, 0x12, 0x73,
+                                0xeb, 0x25, 0xad, 0x05, 0x27, 0xd3, 0xa7, 0xbd,
+                                0x33, 0xf6, 0x72, 0x08, 0xdd, 0x16, 0x66, 0xd9,
+                                0x90, 0x4c, 0x6b, 0xc0, 0x49, 0x69, 0xae, 0x58,
+                                0x31, 0xa8, 0xb8, 0x49, 0xe7, 0xff, 0x64, 0x25,
+                                0x81, 0xf2, 0xc3, 0xe5, 0x6b, 0xe8, 0x46, 0x09,
+                                0x60, 0x0d, 0x3c, 0x6b, 0xbd, 0xad, 0xed, 0x3f,
+                                0x69, 0x89, 0xc3, 0x7d, 0x28, 0x92, 0xb1, 0xe9,
+                                0x78, 0xd5 };
+
+       unsigned char expected_enc[] = {
+               0x04, 0x00, 0x0a, 0x50, 0x96, 0xa6, 0xe6, 0xe0, 0x02, 0xc8,
+               0x35, 0x17, 0xb4, 0x94, 0xbf, 0xc2, 0xe3, 0x6b, 0xfb, 0x86,
+               0x32, 0xfa, 0xe8, 0x06, 0x83, 0x62, 0x85, 0x2b, 0x70, 0xd0,
+               0xff, 0x71, 0xe5, 0x60, 0xb1, 0x5a, 0xff, 0x96, 0x74, 0x1e,
+               0xcf, 0xfb, 0x63, 0xd8, 0xac, 0x30, 0x90, 0xc3, 0x76, 0x96,
+               0x79, 0x00, 0x9a, 0xc5, 0x9a, 0x99, 0xa1, 0xfe, 0xb4, 0x71,
+               0x3c, 0x5f, 0x09, 0x0f, 0xc0, 0xdb, 0xed, 0x01, 0xad, 0x73,
+               0xc4, 0x5d, 0x29, 0xd3, 0x69, 0xe3, 0x67, 0x44, 0xe9, 0xed,
+               0x37, 0xd1, 0x2f, 0x80, 0x70, 0x0c, 0x16, 0xd8, 0x16, 0x48,
+               0x56, 0x55, 0x16, 0x9a, 0x5d, 0xd6, 0x6e, 0x4d, 0xdf, 0x27,
+               0xf2, 0xac, 0xff, 0xe0, 0xf5, 0x6f, 0x7f, 0x77, 0xea, 0x2b,
+               0x47, 0x3b, 0x4b, 0xf0, 0x51, 0x8b, 0x97, 0x5d, 0x95, 0x27,
+               0x00, 0x9a, 0x3d, 0x14, 0xe5, 0xa4, 0x95, 0x7e, 0x3e, 0x8a,
+               0x90, 0x74, 0xf8
+       };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_P521,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA512,
+               .aead = GNUTLS_HPKE_AEAD_AES_256_GCM,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = enc_params,
+               .num_encryption_parameters =
+                       sizeof(enc_params) / sizeof(enc_params[0]),
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a71(void)
+{
+       unsigned char expected_exporter_value1[] = {
+               0x7a, 0x36, 0x22, 0x1b, 0xd5, 0x6d, 0x50, 0xfb,
+               0x51, 0xee, 0x65, 0xed, 0xfd, 0x98, 0xd0, 0x6a,
+               0x23, 0xc4, 0xdc, 0x87, 0x08, 0x5a, 0xa5, 0x86,
+               0x6c, 0xb7, 0x08, 0x72, 0x44, 0xbd, 0x2a, 0x36
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0xd5, 0x53, 0x5b, 0x87, 0x09, 0x9c, 0x6c, 0x3c,
+               0xe8, 0x0d, 0xc1, 0x12, 0xa2, 0x67, 0x1c, 0x6e,
+               0xc8, 0xe8, 0x11, 0xa2, 0xf2, 0x84, 0xf9, 0x48,
+               0xce, 0xc6, 0xdd, 0x17, 0x08, 0xee, 0x33, 0xf0
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xff, 0xaa, 0xbc, 0x85, 0xa7, 0x76, 0x13, 0x6c,
+               0xa0, 0xc3, 0x78, 0xe5, 0xd0, 0x84, 0xc9, 0x14,
+               0x0a, 0xb5, 0x52, 0xb7, 0x8f, 0x03, 0x9d, 0x2e,
+               0x87, 0x75, 0xf2, 0x6e, 0xff, 0xf4, 0xc7, 0x0e
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x55, 0xbc, 0x24, 0x5e, 0xe4, 0xef, 0xda, 0x25,
+               0xd3, 0x8f, 0x2d, 0x54, 0xd5, 0xbb, 0x66, 0x65,
+               0x29, 0x1b, 0x99, 0xf8, 0x10, 0x8a, 0x8c, 0x4b,
+               0x68, 0x6c, 0x2b, 0x14, 0x89, 0x3e, 0xa5, 0xd9
+       };
+
+       unsigned char ikmR[] = {
+               0x68, 0x3a, 0xe0, 0xda, 0x1d, 0x22, 0x18, 0x1e,
+               0x74, 0xed, 0x2e, 0x50, 0x3e, 0xbf, 0x82, 0x84,
+               0x0d, 0xeb, 0x1d, 0x5e, 0x87, 0x2c, 0xad, 0xe2,
+               0x0f, 0x4b, 0x45, 0x8d, 0x99, 0x78, 0x3e, 0x31
+       };
+
+       unsigned char expected_enc[] = { 0xe5, 0xe8, 0xf9, 0xbf, 0xff, 0x6c,
+                                        0x2f, 0x29, 0x79, 0x1f, 0xc3, 0x51,
+                                        0xd2, 0xc2, 0x5c, 0xe1, 0x29, 0x9a,
+                                        0xa5, 0xea, 0xca, 0x78, 0xa7, 0x57,
+                                        0xc0, 0xb4, 0xfb, 0x4b, 0xcd, 0x83,
+                                        0x09, 0x18 };
+
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_BASE,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_EXPORT_ONLY,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = NULL,
+               .num_encryption_parameters = 0,
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a72(void)
+{
+       unsigned char expected_exporter_value1[] = {
+               0xbe, 0x6c, 0x76, 0x95, 0x53, 0x34, 0x37, 0x6a,
+               0xa2, 0x3e, 0x93, 0x6b, 0xe0, 0x13, 0xba, 0x8b,
+               0xba, 0xe9, 0x0a, 0xe7, 0x4e, 0xd9, 0x95, 0xc1,
+               0xc6, 0x15, 0x7e, 0x6f, 0x08, 0xdd, 0x53, 0x16
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x17, 0x21, 0xed, 0x2a, 0xa8, 0x52, 0xf8, 0x4d,
+               0x44, 0xad, 0x02, 0x0c, 0x2e, 0x2b, 0xe4, 0xe2,
+               0xe6, 0x37, 0x50, 0x98, 0xbf, 0x48, 0x77, 0x5a,
+               0x53, 0x35, 0x05, 0xfd, 0x56, 0xa3, 0xf4, 0x16
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x7c, 0x9d, 0x79, 0x87, 0x6a, 0x28, 0x85, 0x07,
+               0xb8, 0x1a, 0x5a, 0x52, 0x36, 0x5a, 0x7d, 0x39,
+               0xcc, 0x0f, 0xa3, 0xf0, 0x7e, 0x34, 0x17, 0x29,
+               0x84, 0xf9, 0x6f, 0xec, 0x07, 0xc4, 0x4c, 0xba
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0xc5, 0x12, 0x11, 0xa8, 0x79, 0x9f, 0x6b, 0x8a,
+               0x00, 0x21, 0xfc, 0xba, 0x67, 0x3d, 0x9c, 0x40,
+               0x67, 0xa9, 0x8e, 0xbc, 0x67, 0x94, 0x23, 0x2e,
+               0x5b, 0x06, 0xcb, 0x9f, 0xeb, 0xcb, 0xbd, 0xf5
+       };
+
+       unsigned char ikmR[] = {
+               0x5e, 0x05, 0x16, 0xb1, 0xb2, 0x9c, 0x0e, 0x13,
+               0x38, 0x65, 0x29, 0xda, 0x16, 0x52, 0x52, 0x10,
+               0xc7, 0x96, 0xf7, 0xd6, 0x47, 0xc3, 0x7e, 0xac,
+               0x11, 0x80, 0x23, 0xa6, 0xaa, 0x9e, 0xb8, 0x9a
+       };
+
+       unsigned char expected_enc[] = { 0xd3, 0x80, 0x5a, 0x97, 0xcb, 0xcd,
+                                        0x5f, 0x08, 0xba, 0xbd, 0x21, 0x22,
+                                        0x1d, 0x3e, 0x6b, 0x36, 0x2a, 0x70,
+                                        0x05, 0x72, 0xd1, 0x4f, 0x9b, 0xbe,
+                                        0xb9, 0x4e, 0xc0, 0x78, 0xd0, 0x51,
+                                        0xae, 0x3d };
+
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_EXPORT_ONLY,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = NULL,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = NULL,
+               .num_encryption_parameters = 0,
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a73(void)
+{
+       unsigned char expected_exporter_value1[] = {
+               0x83, 0xc1, 0xba, 0xc0, 0x0a, 0x45, 0xed, 0x4c,
+               0xb6, 0xbd, 0x8a, 0x60, 0x07, 0xd2, 0xce, 0x4e,
+               0xc5, 0x01, 0xf5, 0x5e, 0x48, 0x5c, 0x56, 0x42,
+               0xbd, 0x01, 0xbf, 0x6b, 0x6d, 0x7d, 0x6f, 0x0a
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x08, 0xa1, 0xd1, 0xad, 0x2a, 0xf3, 0xef, 0x5b,
+               0xc4, 0x02, 0x32, 0xa6, 0x4f, 0x92, 0x06, 0x50,
+               0xeb, 0x9b, 0x10, 0x34, 0xfa, 0xc3, 0x89, 0x2f,
+               0x72, 0x9f, 0x79, 0x49, 0x62, 0x1b, 0xf0, 0x6e
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0xff, 0x3b, 0x0e, 0x37, 0xa9, 0x95, 0x42, 0x47,
+               0xfe, 0xa5, 0x3f, 0x25, 0x1b, 0x79, 0x9e, 0x2e,
+               0xdd, 0x35, 0xaa, 0xc7, 0x15, 0x2c, 0x57, 0x95,
+               0x75, 0x1a, 0x3d, 0xa4, 0x24, 0xfe, 0xca, 0x73
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x43, 0xb0, 0x78, 0x91, 0x2a, 0x54, 0xb5, 0x91,
+               0xa7, 0xb0, 0x9b, 0x16, 0xce, 0x89, 0xa1, 0x95,
+               0x5a, 0x9d, 0xd6, 0x0b, 0x29, 0xfb, 0x61, 0x1e,
+               0x04, 0x42, 0x60, 0x04, 0x6e, 0x8b, 0x06, 0x1b
+       };
+
+       unsigned char ikmR[] = {
+               0xfc, 0x94, 0x07, 0xae, 0x72, 0xed, 0x61, 0x49,
+               0x01, 0xeb, 0xf4, 0x42, 0x57, 0xfb, 0x54, 0x0f,
+               0x61, 0x72, 0x84, 0xb5, 0x36, 0x1c, 0xfe, 0xcd,
+               0x62, 0x0b, 0xaf, 0xc4, 0xab, 0xa3, 0x6f, 0x73
+       };
+
+       unsigned char ikmS[] = {
+               0x2f, 0xf4, 0xc3, 0x7a, 0x17, 0xb2, 0xe5, 0x40,
+               0x46, 0xa0, 0x76, 0xbf, 0x5f, 0xea, 0x9c, 0x3d,
+               0x59, 0x25, 0x0d, 0x54, 0xd0, 0xdc, 0x85, 0x72,
+               0xbc, 0x5f, 0x7c, 0x04, 0x63, 0x07, 0x04, 0x0c
+       };
+
+       unsigned char expected_enc[] = { 0x5a, 0xc1, 0x67, 0x1a, 0x55, 0xc5,
+                                        0xc3, 0x87, 0x5a, 0x8a, 0xfe, 0x74,
+                                        0x66, 0x4a, 0xa8, 0xbc, 0x68, 0x83,
+                                        0x0b, 0xe9, 0xde, 0xd0, 0xc5, 0xf6,
+                                        0x33, 0xcd, 0x96, 0x40, 0x0e, 0x8b,
+                                        0x5c, 0x05 };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_EXPORT_ONLY,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = NULL,
+               .psk_id = NULL,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = NULL,
+               .num_encryption_parameters = 0,
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+static void rfc9180_a74(void)
+{
+       unsigned char expected_exporter_value1[] = {
+               0xda, 0xfd, 0x8b, 0xeb, 0x94, 0xc5, 0x80, 0x25,
+               0x35, 0xc2, 0x2f, 0xf4, 0xc1, 0xaf, 0x89, 0x46,
+               0xc9, 0x8d, 0xf2, 0xc4, 0x17, 0xe1, 0x87, 0xc6,
+               0xcc, 0xaf, 0xe4, 0x53, 0x35, 0x81, 0x0b, 0x58
+       };
+
+       unsigned char expected_exporter_value2[] = {
+               0x73, 0x46, 0xbb, 0x0b, 0x56, 0xca, 0xf4, 0x57,
+               0xbc, 0xc1, 0xaa, 0x63, 0xc1, 0xb9, 0x7d, 0x98,
+               0x34, 0x64, 0x4b, 0xda, 0xca, 0xc8, 0xf7, 0x2d,
+               0xbb, 0xe3, 0x46, 0x3e, 0x4e, 0x46, 0xb0, 0xdd
+       };
+
+       unsigned char expected_exporter_value3[] = {
+               0x84, 0xf3, 0x46, 0x6b, 0xd5, 0xa0, 0x3b, 0xde,
+               0x64, 0x44, 0x32, 0x4e, 0x63, 0xd7, 0x56, 0x0e,
+               0x7a, 0xc7, 0x90, 0xda, 0x4e, 0x5b, 0xba, 0xb0,
+               0x1e, 0x7c, 0x4d, 0x57, 0x57, 0x28, 0xc3, 0x4a
+       };
+
+       hpke_test_exporter_parameters_st exporter_params[] = {
+               { .exporter_context = { exporter_context_1,
+                                       sizeof(exporter_context_1) },
+                 .exporter_length = sizeof(expected_exporter_value1),
+                 .expected_exporter_value = { expected_exporter_value1,
+                                              sizeof(expected_exporter_value1) } },
+               { .exporter_context = { exporter_context_2,
+                                       sizeof(exporter_context_2) },
+                 .exporter_length = sizeof(expected_exporter_value2),
+                 .expected_exporter_value = { expected_exporter_value2,
+                                              sizeof(expected_exporter_value2) } },
+               { .exporter_context = { exporter_context_3,
+                                       sizeof(exporter_context_3) },
+                 .exporter_length = sizeof(expected_exporter_value3),
+                 .expected_exporter_value = { expected_exporter_value3,
+                                              sizeof(expected_exporter_value3) } }
+       };
+
+       unsigned char ikmE[] = {
+               0x94, 0xef, 0xae, 0x91, 0xe9, 0x68, 0x11, 0xa3,
+               0xa4, 0x9f, 0xd1, 0xb2, 0x0e, 0xb0, 0x34, 0x4d,
+               0x68, 0xea, 0xd6, 0xac, 0x01, 0x92, 0x2c, 0x23,
+               0x60, 0x77, 0x9a, 0xa1, 0x72, 0x48, 0x7f, 0x40
+       };
+
+       unsigned char ikmR[] = {
+               0x4d, 0xfd, 0xe6, 0xfa, 0xdf, 0xe5, 0xcb, 0x50,
+               0xfc, 0xed, 0x40, 0x34, 0xe8, 0x4e, 0x6d, 0x3a,
+               0x10, 0x4a, 0xa4, 0xbf, 0x29, 0x71, 0x36, 0x00,
+               0x32, 0xc1, 0xc0, 0x58, 0x0e, 0x28, 0x66, 0x63
+       };
+
+       unsigned char ikmS[] = {
+               0x26, 0xc1, 0x2f, 0xef, 0x8d, 0x71, 0xd1, 0x3b,
+               0xbb, 0xf0, 0x8c, 0xe8, 0x15, 0x7a, 0x28, 0x3d,
+               0x5e, 0x67, 0xec, 0xf0, 0xf3, 0x45, 0x36, 0x6b,
+               0x0e, 0x90, 0x34, 0x19, 0x11, 0x11, 0x0f, 0x1b
+       };
+
+       unsigned char expected_enc[] = { 0x81, 0xcb, 0xf4, 0xbd, 0x7e, 0xee,
+                                        0x97, 0xdd, 0x0b, 0x60, 0x02, 0x52,
+                                        0xa1, 0xc9, 0x64, 0xea, 0x18, 0x68,
+                                        0x46, 0x25, 0x2a, 0xbb, 0x34, 0x0b,
+                                        0xe4, 0x70, 0x87, 0xcc, 0x78, 0xf3,
+                                        0xd8, 0x7c };
+
+       gnutls_datum_t ikmS_datum = { ikmS, sizeof(ikmS) };
+       gnutls_datum_t psk_datum = { psk, sizeof(psk) };
+       gnutls_datum_t psk_id_datum = { psk_id, sizeof(psk_id) };
+       hpke_test_parameters_st params = {
+               .mode = GNUTLS_HPKE_MODE_AUTH_PSK,
+               .kem = GNUTLS_HPKE_KEM_DHKEM_X25519,
+               .kdf = GNUTLS_HPKE_KDF_HKDF_SHA256,
+               .aead = GNUTLS_HPKE_AEAD_EXPORT_ONLY,
+               .ikmE = { ikmE, sizeof(ikmE) },
+               .ikmR = { ikmR, sizeof(ikmR) },
+               .ikmS = &ikmS_datum,
+               .info = { info, sizeof(info) },
+               .psk = &psk_datum,
+               .psk_id = &psk_id_datum,
+               .expected_enc = { expected_enc, sizeof(expected_enc) },
+               .encryption_parameters = NULL,
+               .num_encryption_parameters = 0,
+               .exporter_parameters = exporter_params,
+               .num_exporter_parameters =
+                       sizeof(exporter_params) / sizeof(exporter_params[0])
+       };
+
+       test_hpke(&params);
+}
+
+void doit(void)
+{
+       gnutls_global_init();
+
+       rfc9180_a11();
+       rfc9180_a12();
+       rfc9180_a13();
+       rfc9180_a14();
+
+       rfc9180_a31();
+       rfc9180_a32();
+       rfc9180_a33();
+       rfc9180_a34();
+
+       rfc9180_a61();
+       rfc9180_a62();
+       rfc9180_a63();
+       rfc9180_a64();
+
+       rfc9180_a71();
+       rfc9180_a72();
+       rfc9180_a73();
+       rfc9180_a74();
+
+       if (!gnutls_fips140_mode_enabled()) {
+               rfc9180_a21();
+               rfc9180_a22();
+               rfc9180_a23();
+               rfc9180_a24();
+
+               rfc9180_a51();
+               rfc9180_a52();
+               rfc9180_a53();
+               rfc9180_a54();
+       }
+
+       gnutls_global_deinit();
+}
diff --git a/tests/hpke.c b/tests/hpke.c
deleted file mode 100644 (file)
index 86d0f31..0000000
+++ /dev/null
@@ -1,1218 +0,0 @@
-/*
- * Copyright © 2025 David Dudas
- *
- * Author: David Dudas <david.dudas03@e-uvt.ro>
- *
- * This file is part of GnuTLS.
- *
- * The GnuTLS is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * as published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This library 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
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public License
-
- * along with this program.  If not, see <https://www.gnu.org/licenses/>
-
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#include <gnutls/gnutls.h>
-#include <gnutls/crypto.h>
-#include <gnutls/abstract.h>
-
-#include "utils.h"
-
-#include <stdbool.h>
-#include <stdint.h>
-
-#define report_failure(msg, ret)                                            \
-       fprintf(stderr, "%s(%s):%d %s: %s\n", __FILE__, __func__, __LINE__, \
-               msg, gnutls_strerror(ret))
-
-static int get_pk(const gnutls_ecc_curve_t curve)
-{
-       switch (curve) {
-       case GNUTLS_ECC_CURVE_SECP256R1:
-       case GNUTLS_ECC_CURVE_SECP384R1:
-       case GNUTLS_ECC_CURVE_SECP521R1:
-               return GNUTLS_PK_EC;
-       case GNUTLS_ECC_CURVE_X25519:
-               return GNUTLS_PK_ECDH_X25519;
-       case GNUTLS_ECC_CURVE_X448:
-               return GNUTLS_PK_ECDH_X448;
-       default:
-               return GNUTLS_PK_UNKNOWN;
-       }
-}
-
-static int generate_privkey(const gnutls_ecc_curve_t curve,
-                           gnutls_privkey_t *privkey)
-{
-       int ret;
-
-       ret = gnutls_privkey_init(privkey);
-       if (ret < 0) {
-               report_failure("Failed to initialize private key", ret);
-               return ret;
-       }
-
-       unsigned int bits = gnutls_ecc_curve_get_size(curve) * 8;
-       const gnutls_pk_algorithm_t pk_algo = get_pk(curve);
-       if (pk_algo == GNUTLS_PK_UNKNOWN) {
-               ret = GNUTLS_E_INVALID_REQUEST;
-               report_failure("Unsupported curve for private key generation",
-                              ret);
-               gnutls_privkey_deinit(*privkey);
-
-               return ret;
-       }
-
-       ret = gnutls_privkey_generate(*privkey, pk_algo, bits, 0);
-       if (ret < 0) {
-               report_failure("Failed to generate private key", ret);
-               gnutls_privkey_deinit(*privkey);
-
-               return ret;
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static const char *kem_to_string(const gnutls_hpke_kem_t kem)
-{
-       switch (kem) {
-       case GNUTLS_HPKE_KEM_DHKEM_P256:
-               return "DHKEM_P256";
-       case GNUTLS_HPKE_KEM_DHKEM_P384:
-               return "DHKEM_P384";
-       case GNUTLS_HPKE_KEM_DHKEM_P521:
-               return "DHKEM_P521";
-       case GNUTLS_HPKE_KEM_DHKEM_X25519:
-               return "DHKEM_X25519";
-       case GNUTLS_HPKE_KEM_DHKEM_X448:
-               return "DHKEM_X448";
-       default:
-               return "Unknown";
-       }
-}
-
-static const char *kdf_to_string(const gnutls_hpke_kdf_t kdf)
-{
-       switch (kdf) {
-       case GNUTLS_HPKE_KDF_HKDF_SHA256:
-               return "HKDF_SHA256";
-       case GNUTLS_HPKE_KDF_HKDF_SHA384:
-               return "HKDF_SHA384";
-       case GNUTLS_HPKE_KDF_HKDF_SHA512:
-               return "HKDF_SHA512";
-       default:
-               return "Unknown";
-       }
-}
-
-static const char *aead_to_string(const gnutls_hpke_aead_t aead)
-{
-       switch (aead) {
-       case GNUTLS_HPKE_AEAD_AES_128_GCM:
-               return "AES128GCM";
-       case GNUTLS_HPKE_AEAD_AES_256_GCM:
-               return "AES256GCM";
-       case GNUTLS_HPKE_AEAD_CHACHA20_POLY1305:
-               return "CHACHA20POLY1305";
-       default:
-               return "Unknown";
-       }
-}
-
-static int initialize_pubkey(gnutls_pubkey_t *pubkey, gnutls_privkey_t privkey)
-{
-       int ret;
-
-       ret = gnutls_pubkey_init(pubkey);
-       if (ret < 0) {
-               report_failure("Failed to initialize public key", ret);
-
-               return ret;
-       }
-
-       ret = gnutls_pubkey_import_privkey(*pubkey, privkey, 0, 0);
-       if (ret < 0) {
-               report_failure("Failed to import public key from private key",
-                              ret);
-               gnutls_pubkey_deinit(*pubkey);
-
-               return ret;
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static gnutls_ecc_curve_t get_curve_from_kem(gnutls_hpke_kem_t kem)
-{
-       switch (kem) {
-       case GNUTLS_HPKE_KEM_DHKEM_P256:
-               return GNUTLS_ECC_CURVE_SECP256R1;
-       case GNUTLS_HPKE_KEM_DHKEM_P384:
-               return GNUTLS_ECC_CURVE_SECP384R1;
-       case GNUTLS_HPKE_KEM_DHKEM_P521:
-               return GNUTLS_ECC_CURVE_SECP521R1;
-       case GNUTLS_HPKE_KEM_DHKEM_X25519:
-               return GNUTLS_ECC_CURVE_X25519;
-       case GNUTLS_HPKE_KEM_DHKEM_X448:
-               return GNUTLS_ECC_CURVE_X448;
-       default:
-               return GNUTLS_ECC_CURVE_INVALID;
-       }
-}
-
-static int generate_keys(gnutls_ecc_curve_t curve, gnutls_privkey_t *privkey,
-                        gnutls_pubkey_t *pubkey)
-{
-       int ret;
-
-       ret = generate_privkey(curve, privkey);
-       if (ret < 0) {
-               report_failure("Failed to generate private key", ret);
-               return ret;
-       }
-
-       ret = initialize_pubkey(pubkey, *privkey);
-       if (ret < 0) {
-               report_failure("Failed to initialize public key", ret);
-               gnutls_privkey_deinit(*privkey);
-
-               return ret;
-       }
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static bool compare_datum(const gnutls_datum_t *a, const gnutls_datum_t *b)
-{
-       if (a->size != b->size) {
-               return false;
-       }
-
-       return memcmp(a->data, b->data, a->size) == 0;
-}
-
-static bool test_hpke_base(const gnutls_hpke_kem_t kem,
-                          const gnutls_hpke_kdf_t kdf,
-                          const gnutls_hpke_aead_t aead,
-                          const gnutls_datum_t *info_used_by_sender,
-                          const gnutls_datum_t *info_used_by_receiver,
-                          const gnutls_datum_t *pkE_used_by_receiver)
-{
-       int ret;
-       bool result = false;
-       gnutls_privkey_t skR = NULL;
-       gnutls_pubkey_t pkR = NULL;
-       gnutls_datum_t pkE = { NULL, 0 };
-       gnutls_datum_t encap_result_key = { NULL, 0 };
-       gnutls_datum_t encap_base_nonce = { NULL, 0 };
-       gnutls_datum_t encap_exporter_secret = { NULL, 0 };
-       gnutls_datum_t decap_result_key = { NULL, 0 };
-       gnutls_datum_t decap_base_nonce = { NULL, 0 };
-       gnutls_datum_t decap_exporter_secret = { NULL, 0 };
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               report_failure("Invalid curve for the given KEM",
-                              GNUTLS_E_INVALID_REQUEST);
-               return false;
-       }
-
-       ret = generate_keys(curve, &skR, &pkR);
-       if (ret < 0) {
-               report_failure("Failed to generate keys", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_encap_context_t encap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_sender,
-
-               .receiver_pubkey = pkR,
-       };
-
-       ret = gnutls_hpke_encap(&encap_ctx, &pkE, &encap_result_key,
-                               &encap_base_nonce, &encap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to encapsulate public key", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_decap_context_t decap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_receiver,
-               .enc = pkE_used_by_receiver == NULL ? &pkE :
-                                                     pkE_used_by_receiver,
-
-               .receiver_privkey = skR,
-       };
-
-       ret = gnutls_hpke_decap(&decap_ctx, &decap_result_key,
-                               &decap_base_nonce, &decap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to decapsulate private key", ret);
-               goto cleanup;
-       }
-
-       result = compare_datum(&encap_result_key, &decap_result_key) &&
-                compare_datum(&encap_base_nonce, &decap_base_nonce) &&
-                compare_datum(&encap_exporter_secret, &decap_exporter_secret);
-
-cleanup:
-
-       if (encap_result_key.data) {
-               gnutls_free(encap_result_key.data);
-       }
-
-       if (decap_result_key.data) {
-               gnutls_free(decap_result_key.data);
-       }
-
-       if (encap_base_nonce.data) {
-               gnutls_free(encap_base_nonce.data);
-       }
-
-       if (decap_base_nonce.data) {
-               gnutls_free(decap_base_nonce.data);
-       }
-
-       if (encap_exporter_secret.data) {
-               gnutls_free(encap_exporter_secret.data);
-       }
-
-       if (decap_exporter_secret.data) {
-               gnutls_free(decap_exporter_secret.data);
-       }
-
-       if (pkE.data) {
-               gnutls_free(pkE.data);
-       }
-
-       if (pkR != NULL) {
-               gnutls_pubkey_deinit(pkR);
-       }
-
-       if (skR != NULL) {
-               gnutls_privkey_deinit(skR);
-       }
-
-       return result;
-}
-
-static bool test_hpke_psk(const gnutls_hpke_kem_t kem,
-                         const gnutls_hpke_kdf_t kdf,
-                         const gnutls_hpke_aead_t aead,
-                         const gnutls_datum_t *info_used_by_sender,
-                         const gnutls_datum_t *info_used_by_receiver,
-                         const gnutls_datum_t *psk_used_by_sender,
-                         const gnutls_datum_t *psk_used_by_receiver,
-                         const gnutls_datum_t *pkE_used_by_receiver)
-{
-       int ret;
-       bool result = false;
-       gnutls_privkey_t skR = NULL;
-       gnutls_pubkey_t pkR = NULL;
-       gnutls_datum_t pkE = { NULL, 0 };
-       gnutls_datum_t encap_result_key = { NULL, 0 };
-       gnutls_datum_t encap_base_nonce = { NULL, 0 };
-       gnutls_datum_t encap_exporter_secret = { NULL, 0 };
-       gnutls_datum_t decap_result_key = { NULL, 0 };
-       gnutls_datum_t decap_base_nonce = { NULL, 0 };
-       gnutls_datum_t decap_exporter_secret = { NULL, 0 };
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               report_failure("Invalid curve for the given KEM",
-                              GNUTLS_E_INVALID_REQUEST);
-               return false;
-       }
-
-       ret = generate_keys(curve, &skR, &pkR);
-       if (ret < 0) {
-               report_failure("Failed to generate keys", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_encap_context_t encap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_sender,
-               .psk = psk_used_by_sender,
-               .psk_id = &(gnutls_datum_t){ (unsigned char *)"psk_id", 6 },
-
-               .receiver_pubkey = pkR,
-       };
-
-       ret = gnutls_hpke_encap(&encap_ctx, &pkE, &encap_result_key,
-                               &encap_base_nonce, &encap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to encapsulate public key", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_decap_context_t decap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_receiver,
-               .psk = psk_used_by_receiver,
-               .psk_id = &(gnutls_datum_t){ (unsigned char *)"psk_id", 6 },
-
-               .enc = pkE_used_by_receiver == NULL ? &pkE :
-                                                     pkE_used_by_receiver,
-
-               .receiver_privkey = skR,
-       };
-
-       ret = gnutls_hpke_decap(&decap_ctx, &decap_result_key,
-                               &decap_base_nonce, &decap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to decapsulate private key", ret);
-               goto cleanup;
-       }
-
-       result = compare_datum(&encap_result_key, &decap_result_key) &&
-                compare_datum(&encap_base_nonce, &decap_base_nonce) &&
-                compare_datum(&encap_exporter_secret, &decap_exporter_secret);
-
-cleanup:
-
-       if (encap_result_key.data) {
-               gnutls_free(encap_result_key.data);
-       }
-
-       if (decap_result_key.data) {
-               gnutls_free(decap_result_key.data);
-       }
-
-       if (encap_base_nonce.data) {
-               gnutls_free(encap_base_nonce.data);
-       }
-
-       if (decap_base_nonce.data) {
-               gnutls_free(decap_base_nonce.data);
-       }
-
-       if (encap_exporter_secret.data) {
-               gnutls_free(encap_exporter_secret.data);
-       }
-
-       if (decap_exporter_secret.data) {
-               gnutls_free(decap_exporter_secret.data);
-       }
-
-       if (pkE.data) {
-               gnutls_free(pkE.data);
-       }
-
-       if (pkR != NULL) {
-               gnutls_pubkey_deinit(pkR);
-       }
-
-       if (skR != NULL) {
-               gnutls_privkey_deinit(skR);
-       }
-
-       return result;
-}
-
-static bool test_hpke_auth(const gnutls_hpke_kem_t kem,
-                          const gnutls_hpke_kdf_t kdf,
-                          const gnutls_hpke_aead_t aead,
-                          const gnutls_privkey_t skS,
-                          const gnutls_pubkey_t pkS,
-                          const gnutls_datum_t *info_used_by_sender,
-                          const gnutls_datum_t *info_used_by_receiver,
-                          const gnutls_datum_t *pkE_used_by_receiver)
-{
-       int ret;
-       bool result = false;
-       gnutls_privkey_t skR = NULL;
-       gnutls_pubkey_t pkR = NULL;
-       gnutls_datum_t pkE = { NULL, 0 };
-       gnutls_datum_t encap_result_key = { NULL, 0 };
-       gnutls_datum_t encap_base_nonce = { NULL, 0 };
-       gnutls_datum_t encap_exporter_secret = { NULL, 0 };
-       gnutls_datum_t decap_result_key = { NULL, 0 };
-       gnutls_datum_t decap_base_nonce = { NULL, 0 };
-       gnutls_datum_t decap_exporter_secret = { NULL, 0 };
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               report_failure("Invalid curve for the given KEM",
-                              GNUTLS_E_INVALID_REQUEST);
-               return false;
-       }
-
-       ret = generate_keys(curve, &skR, &pkR);
-       if (ret < 0) {
-               report_failure("Failed to generate keys", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_encap_context_t encap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_sender,
-
-               .sender_privkey = skS,
-               .receiver_pubkey = pkR,
-       };
-
-       ret = gnutls_hpke_encap(&encap_ctx, &pkE, &encap_result_key,
-                               &encap_base_nonce, &encap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to encapsulate public key", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_decap_context_t decap_ctx = {
-
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_receiver,
-
-               .enc = pkE_used_by_receiver == NULL ? &pkE :
-                                                     pkE_used_by_receiver,
-               .receiver_privkey = skR,
-               .sender_pubkey = pkS,
-       };
-
-       ret = gnutls_hpke_decap(&decap_ctx, &decap_result_key,
-                               &decap_base_nonce, &decap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to decapsulate private key", ret);
-               goto cleanup;
-       }
-
-       result = compare_datum(&encap_result_key, &decap_result_key) &&
-                compare_datum(&encap_base_nonce, &decap_base_nonce) &&
-                compare_datum(&encap_exporter_secret, &decap_exporter_secret);
-
-cleanup:
-
-       if (encap_result_key.data) {
-               gnutls_free(encap_result_key.data);
-       }
-
-       if (decap_result_key.data) {
-               gnutls_free(decap_result_key.data);
-       }
-
-       if (encap_base_nonce.data) {
-               gnutls_free(encap_base_nonce.data);
-       }
-
-       if (decap_base_nonce.data) {
-               gnutls_free(decap_base_nonce.data);
-       }
-
-       if (encap_exporter_secret.data) {
-               gnutls_free(encap_exporter_secret.data);
-       }
-
-       if (decap_exporter_secret.data) {
-               gnutls_free(decap_exporter_secret.data);
-       }
-
-       if (pkE.data) {
-               gnutls_free(pkE.data);
-       }
-
-       if (pkR != NULL) {
-               gnutls_pubkey_deinit(pkR);
-       }
-
-       if (skR != NULL) {
-               gnutls_privkey_deinit(skR);
-       }
-
-       return result;
-}
-
-static bool test_hpke_psk_auth(const gnutls_hpke_kem_t kem,
-                              const gnutls_hpke_kdf_t kdf,
-                              const gnutls_hpke_aead_t aead,
-                              const gnutls_privkey_t skS,
-                              const gnutls_pubkey_t pkS,
-                              const gnutls_datum_t *info_used_by_sender,
-                              const gnutls_datum_t *info_used_by_receiver,
-                              const gnutls_datum_t *psk_used_by_sender,
-                              const gnutls_datum_t *psk_used_by_receiver,
-                              const gnutls_datum_t *pkE_used_by_receiver)
-{
-       int ret;
-       bool result = false;
-       gnutls_privkey_t skR = NULL;
-       gnutls_pubkey_t pkR = NULL;
-       gnutls_datum_t pkE = { NULL, 0 };
-       gnutls_datum_t encap_result_key = { NULL, 0 };
-       gnutls_datum_t encap_base_nonce = { NULL, 0 };
-       gnutls_datum_t encap_exporter_secret = { NULL, 0 };
-       gnutls_datum_t decap_result_key = { NULL, 0 };
-       gnutls_datum_t decap_base_nonce = { NULL, 0 };
-       gnutls_datum_t decap_exporter_secret = { NULL, 0 };
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               report_failure("Invalid curve for the given KEM",
-                              GNUTLS_E_INVALID_REQUEST);
-               return false;
-       }
-
-       ret = generate_keys(curve, &skR, &pkR);
-       if (ret < 0) {
-               report_failure("Failed to generate keys", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_encap_context_t encap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_sender,
-               .psk = psk_used_by_sender,
-               .psk_id = &(gnutls_datum_t){ (unsigned char *)"psk_id", 6 },
-
-               .sender_privkey = skS,
-               .receiver_pubkey = pkR,
-       };
-
-       ret = gnutls_hpke_encap(&encap_ctx, &pkE, &encap_result_key,
-                               &encap_base_nonce, &encap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to encapsulate public key", ret);
-               goto cleanup;
-       }
-
-       const gnutls_hpke_decap_context_t decap_ctx = {
-               .kem = kem,
-               .kdf = kdf,
-               .aead = aead,
-
-               .info = info_used_by_receiver,
-               .psk = psk_used_by_receiver,
-               .psk_id = &(gnutls_datum_t){ (unsigned char *)"psk_id", 6 },
-
-               .enc = pkE_used_by_receiver == NULL ? &pkE :
-                                                     pkE_used_by_receiver,
-               .receiver_privkey = skR,
-               .sender_pubkey = pkS,
-       };
-
-       ret = gnutls_hpke_decap(&decap_ctx, &decap_result_key,
-                               &decap_base_nonce, &decap_exporter_secret);
-       if (ret < 0) {
-               report_failure("Failed to encapsulate public key", ret);
-               goto cleanup;
-       }
-
-       result = compare_datum(&encap_result_key, &decap_result_key) &&
-                compare_datum(&encap_base_nonce, &decap_base_nonce) &&
-                compare_datum(&encap_exporter_secret, &decap_exporter_secret);
-
-cleanup:
-
-       if (encap_result_key.data) {
-               gnutls_free(encap_result_key.data);
-       }
-
-       if (decap_result_key.data) {
-               gnutls_free(decap_result_key.data);
-       }
-
-       if (encap_base_nonce.data) {
-               gnutls_free(encap_base_nonce.data);
-       }
-
-       if (decap_base_nonce.data) {
-               gnutls_free(decap_base_nonce.data);
-       }
-
-       if (encap_exporter_secret.data) {
-               gnutls_free(encap_exporter_secret.data);
-       }
-
-       if (decap_exporter_secret.data) {
-               gnutls_free(decap_exporter_secret.data);
-       }
-
-       if (pkE.data) {
-               gnutls_free(pkE.data);
-       }
-
-       if (pkR != NULL) {
-               gnutls_pubkey_deinit(pkR);
-       }
-
-       if (skR != NULL) {
-               gnutls_privkey_deinit(skR);
-       }
-
-       return result;
-}
-
-static void test_hpke_base_mode_keys_should_match(const gnutls_hpke_kem_t kem,
-                                                 const gnutls_hpke_kdf_t kdf,
-                                                 const gnutls_hpke_aead_t aead)
-{
-       if (!test_hpke_base(kem, kdf, aead, NULL, NULL, NULL)) {
-               fail("HPKE base mode test failed; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static int _gnutls_coord_pad_left(const gnutls_datum_t *in, const int out_size,
-                                 gnutls_datum_t *out)
-{
-       if ((int)in->size > out_size) {
-               return GNUTLS_E_INVALID_REQUEST;
-       }
-
-       out->size = out_size;
-       out->data = gnutls_malloc(out->size);
-       if (out->data == NULL) {
-               return GNUTLS_E_MEMORY_ERROR;
-       }
-
-       memset(out->data, 0, out->size - in->size);
-       memcpy(out->data + (out->size - in->size), in->data, in->size);
-
-       return GNUTLS_E_SUCCESS;
-}
-
-static int _gnutls_pubkey_to_datum(const gnutls_pubkey_t pk,
-                                  gnutls_datum_t *datum)
-{
-       int ret = 0;
-       gnutls_ecc_curve_t curve;
-       gnutls_datum_t x = { NULL, 0 };
-       gnutls_datum_t y = { NULL, 0 };
-       gnutls_datum_t x_padded = { NULL, 0 };
-       gnutls_datum_t y_padded = { NULL, 0 };
-
-       ret = gnutls_pubkey_export_ecc_raw2(pk, &curve, &x, &y,
-                                           GNUTLS_EXPORT_FLAG_NO_LZ);
-       if (ret < 0) {
-               goto cleanup;
-       }
-
-       if (curve == GNUTLS_ECC_CURVE_X25519 ||
-           curve == GNUTLS_ECC_CURVE_X448) {
-               datum->size = x.size;
-               datum->data = gnutls_malloc(datum->size);
-               if (datum->data == NULL) {
-                       ret = GNUTLS_E_MEMORY_ERROR;
-                       goto cleanup;
-               }
-
-               memcpy(datum->data, x.data, x.size);
-               goto cleanup;
-       }
-
-       int coord_size = gnutls_ecc_curve_get_size(curve);
-       ret = _gnutls_coord_pad_left(&x, coord_size, &x_padded);
-       if (ret < 0) {
-               goto cleanup;
-       }
-
-       ret = _gnutls_coord_pad_left(&y, coord_size, &y_padded);
-       if (ret < 0) {
-               goto cleanup;
-       }
-
-       datum->size = 1 + x_padded.size + y_padded.size;
-       datum->data = gnutls_malloc(datum->size);
-       if (datum->data == NULL) {
-               ret = GNUTLS_E_MEMORY_ERROR;
-               goto cleanup;
-       }
-
-       datum->data[0] = 0x04;
-       memcpy(datum->data + 1, x_padded.data, x_padded.size);
-       memcpy(datum->data + 1 + x_padded.size, y_padded.data, y_padded.size);
-
-cleanup:
-       if (x.data != NULL) {
-               gnutls_free(x.data);
-       }
-
-       if (y.data != NULL) {
-               gnutls_free(y.data);
-       }
-
-       if (x_padded.data != NULL) {
-               gnutls_free(x_padded.data);
-       }
-
-       if (y_padded.data != NULL) {
-               gnutls_free(y_padded.data);
-       }
-
-       return ret;
-}
-
-static void
-test_hpke_base_mode_keys_should_not_match_if_different_pkE_is_used_for_decap(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead)
-{
-       int ret;
-       gnutls_privkey_t skR = NULL;
-       gnutls_pubkey_t pkR = NULL;
-       gnutls_datum_t pkE_datum = { NULL, 0 };
-
-       const gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               report_failure("Invalid curve for the given KEM",
-                              GNUTLS_E_INVALID_REQUEST);
-               return;
-       }
-
-       ret = generate_keys(curve, &skR, &pkR);
-       if (ret < 0) {
-               report_failure("Failed to generate keys", ret);
-               return;
-       }
-
-       ret = _gnutls_pubkey_to_datum(pkR, &pkE_datum);
-       if (ret < 0) {
-               report_failure("Failed to convert public key to datum", ret);
-               if (pkR != NULL) {
-                       gnutls_pubkey_deinit(pkR);
-               }
-
-               if (skR != NULL) {
-                       gnutls_privkey_deinit(skR);
-               }
-
-               return;
-       }
-
-       const bool succes =
-               !test_hpke_base(kem, kdf, aead, NULL, NULL, &pkE_datum);
-
-       gnutls_free(pkE_datum.data);
-
-       if (pkR != NULL) {
-               gnutls_pubkey_deinit(pkR);
-       }
-
-       if (skR != NULL) {
-               gnutls_privkey_deinit(skR);
-       }
-
-       if (!succes) {
-               fail("HPKE base mode keys should not match if different pkE is used for decap; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static void suite_hpke_base_mode(const gnutls_hpke_kem_t kem,
-                                const gnutls_hpke_kdf_t kdf,
-                                const gnutls_hpke_aead_t aead)
-{
-       test_hpke_base_mode_keys_should_match(kem, kdf, aead);
-       test_hpke_base_mode_keys_should_not_match_if_different_pkE_is_used_for_decap(
-               kem, kdf, aead);
-}
-
-static void test_hpke_psk_mode_keys_should_match(const gnutls_hpke_kem_t kem,
-                                                const gnutls_hpke_kdf_t kdf,
-                                                const gnutls_hpke_aead_t aead,
-                                                const uint8_t *psk)
-{
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-
-       if (!test_hpke_psk(kem, kdf, aead, NULL, NULL, &psk_datum, &psk_datum,
-                          NULL)) {
-               fail("HPKE PSK keys do not match\n");
-       }
-}
-
-static void
-test_hpke_psk_mode_keys_should_not_match_if_different_psk_is_used_for_decap(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk, const uint8_t *psk2)
-{
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-       gnutls_datum_t psk2_datum = { (uint8_t *)psk2, 32 };
-
-       if (test_hpke_psk(kem, kdf, aead, NULL, NULL, &psk_datum, &psk2_datum,
-                         NULL)) {
-               fail("HPKE PSK keys should not match if different PSK is used for decap\n");
-       }
-}
-
-static void test_hpke_psk_mode_keys_should_match_if_same_info_is_used(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk)
-{
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-       gnutls_datum_t info = { (uint8_t *)"test info", 9 };
-
-       if (!test_hpke_psk(kem, kdf, aead, &info, &info, &psk_datum, &psk_datum,
-                          NULL)) {
-               fail("HPKE PSK keys do not match when same info is used\n");
-       }
-}
-
-static void test_hpke_psk_mode_keys_should_not_match_if_different_info_is_used(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk)
-{
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-       const gnutls_datum_t info1 = { (uint8_t *)"test info 1", 11 };
-       const gnutls_datum_t info2 = { (uint8_t *)"test info 2", 11 };
-
-       if (test_hpke_psk(kem, kdf, aead, &info1, &info2, &psk_datum,
-                         &psk_datum, NULL)) {
-               fail("HPKE PSK keys should not match when different info is used\n");
-       }
-}
-
-static void suite_hpke_psk_mode(const gnutls_hpke_kem_t kem,
-                               const gnutls_hpke_kdf_t kdf,
-                               const gnutls_hpke_aead_t aead)
-{
-       const uint8_t psk[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-                                 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
-                                 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
-                                 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
-                                 0x1d, 0x1e, 0x1f, 0x20 };
-
-       const uint8_t psk2[32] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
-                                  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
-                                  0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
-                                  0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
-                                  0x3d, 0x3e, 0x3f, 0x40 };
-
-       test_hpke_psk_mode_keys_should_match(kem, kdf, aead, psk);
-       test_hpke_psk_mode_keys_should_not_match_if_different_psk_is_used_for_decap(
-               kem, kdf, aead, psk, psk2);
-
-       test_hpke_psk_mode_keys_should_match_if_same_info_is_used(kem, kdf,
-                                                                 aead, psk);
-       test_hpke_psk_mode_keys_should_not_match_if_different_info_is_used(
-               kem, kdf, aead, psk);
-}
-
-static void test_hpke_auth_mode_keys_should_match(const gnutls_hpke_kem_t kem,
-                                                 const gnutls_hpke_kdf_t kdf,
-                                                 const gnutls_hpke_aead_t aead)
-{
-       int ret;
-       gnutls_privkey_t skS = NULL;
-       gnutls_pubkey_t pkS = NULL;
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               fail("Invalid curve for the given KEM\n");
-       }
-
-       ret = generate_keys(curve, &skS, &pkS);
-       if (ret < 0) {
-               fail("Failed to generate keys: %s\n", gnutls_strerror(ret));
-       }
-
-       const bool pass =
-               test_hpke_auth(kem, kdf, aead, skS, pkS, NULL, NULL, NULL);
-
-       if (pkS != NULL) {
-               gnutls_pubkey_deinit(pkS);
-       }
-
-       if (skS != NULL) {
-               gnutls_privkey_deinit(skS);
-       }
-
-       if (!pass) {
-               fail("HPKE Auth keys do not match; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static void test_hpke_auth_mode_keys_should_not_match_if_wrong_pkS_is_used(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead)
-{
-       int ret;
-       gnutls_privkey_t skS = NULL;
-       gnutls_pubkey_t pkS = NULL;
-       gnutls_privkey_t skS_wrong = NULL;
-       gnutls_pubkey_t pkS_wrong = NULL;
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               fail("Invalid curve for the given KEM\n");
-       }
-
-       ret = generate_keys(curve, &skS, &pkS);
-       if (ret < 0) {
-               fail("Failed to generate keys: %s\n", gnutls_strerror(ret));
-       }
-
-       ret = generate_keys(curve, &skS_wrong, &pkS_wrong);
-       if (ret < 0) {
-               fail("Failed to generate wrong keys: %s\n",
-                    gnutls_strerror(ret));
-       }
-
-       bool pass = !test_hpke_auth(kem, kdf, aead, skS, pkS_wrong, NULL, NULL,
-                                   NULL);
-
-       if (pkS != NULL) {
-               gnutls_pubkey_deinit(pkS);
-       }
-
-       if (skS != NULL) {
-               gnutls_privkey_deinit(skS);
-       }
-
-       if (pkS_wrong != NULL) {
-               gnutls_pubkey_deinit(pkS_wrong);
-       }
-
-       if (skS_wrong != NULL) {
-               gnutls_privkey_deinit(skS_wrong);
-       }
-
-       if (!pass) {
-               fail("HPKE Auth keys should not match when wrong pkS is used; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static void suite_hpke_auth_mode(const gnutls_hpke_kem_t kem,
-                                const gnutls_hpke_kdf_t kdf,
-                                const gnutls_hpke_aead_t aead)
-{
-       test_hpke_auth_mode_keys_should_match(kem, kdf, aead);
-       test_hpke_auth_mode_keys_should_not_match_if_wrong_pkS_is_used(kem, kdf,
-                                                                      aead);
-}
-
-static void test_hpke_auth_psk_mode_keys_should_match(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk)
-{
-       int ret;
-       gnutls_privkey_t skS = NULL;
-       gnutls_pubkey_t pkS = NULL;
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               fail("Invalid curve for the given KEM\n");
-       }
-
-       ret = generate_keys(curve, &skS, &pkS);
-       if (ret < 0) {
-               fail("Failed to generate keys: %s\n", gnutls_strerror(ret));
-       }
-
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-
-       const bool pass = test_hpke_psk_auth(kem, kdf, aead, skS, pkS, NULL,
-                                            NULL, &psk_datum, &psk_datum,
-                                            NULL);
-
-       if (pkS != NULL) {
-               gnutls_pubkey_deinit(pkS);
-       }
-
-       if (skS != NULL) {
-               gnutls_privkey_deinit(skS);
-       }
-
-       if (!pass) {
-               fail("HPKE AuthPSK keys do not match; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static void test_hpke_auth_psk_mode_keys_should_not_match_if_wrong_pkS_is_used(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk)
-{
-       int ret;
-       gnutls_privkey_t skS = NULL;
-       gnutls_pubkey_t pkS = NULL;
-       gnutls_privkey_t skS_wrong = NULL;
-       gnutls_pubkey_t pkS_wrong = NULL;
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               fail("Invalid curve for the given KEM\n");
-       }
-
-       ret = generate_keys(curve, &skS, &pkS);
-       if (ret < 0) {
-               fail("Failed to generate keys: %s\n", gnutls_strerror(ret));
-       }
-
-       ret = generate_keys(curve, &skS_wrong, &pkS_wrong);
-       if (ret < 0) {
-               fail("Failed to generate wrong keys: %s\n",
-                    gnutls_strerror(ret));
-       }
-
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-
-       bool pass = !test_hpke_psk_auth(kem, kdf, aead, skS, pkS_wrong, NULL,
-                                       NULL, &psk_datum, &psk_datum, NULL);
-
-       if (pkS != NULL) {
-               gnutls_pubkey_deinit(pkS);
-       }
-
-       if (skS != NULL) {
-               gnutls_privkey_deinit(skS);
-       }
-
-       if (pkS_wrong != NULL) {
-               gnutls_pubkey_deinit(pkS_wrong);
-       }
-
-       if (skS_wrong != NULL) {
-               gnutls_privkey_deinit(skS_wrong);
-       }
-
-       if (!pass) {
-               fail("HPKE AuthPSK keys should not match when wrong pkS is used; params: %s, %s, %s\n",
-                    kem_to_string(kem), kdf_to_string(kdf),
-                    aead_to_string(aead));
-       }
-}
-
-static void
-test_hpke_auth_psk_mode_keys_should_not_match_if_different_psk_is_used_for_decap(
-       const gnutls_hpke_kem_t kem, const gnutls_hpke_kdf_t kdf,
-       const gnutls_hpke_aead_t aead, const uint8_t *psk, const uint8_t *psk2)
-{
-       int ret;
-       gnutls_privkey_t skS = NULL;
-       gnutls_pubkey_t pkS = NULL;
-
-       gnutls_ecc_curve_t curve = get_curve_from_kem(kem);
-       if (curve == GNUTLS_ECC_CURVE_INVALID) {
-               fail("Invalid curve for the given KEM\n");
-       }
-
-       ret = generate_keys(curve, &skS, &pkS);
-       if (ret < 0) {
-               fail("Failed to generate keys: %s\n", gnutls_strerror(ret));
-       }
-
-       gnutls_datum_t psk_datum = { (uint8_t *)psk, 32 };
-       gnutls_datum_t psk2_datum = { (uint8_t *)psk2, 32 };
-
-       bool pass = !test_hpke_psk_auth(kem, kdf, aead, skS, pkS, NULL, NULL,
-                                       &psk_datum, &psk2_datum, NULL);
-
-       if (pkS != NULL) {
-               gnutls_pubkey_deinit(pkS);
-       }
-
-       if (skS != NULL) {
-               gnutls_privkey_deinit(skS);
-       }
-
-       if (!pass) {
-               fail("HPKE AuthPSK keys should not match if different PSK is used for decap\n");
-       }
-}
-
-static void suite_hpke_auth_psk_mode(const gnutls_hpke_kem_t kem,
-                                    const gnutls_hpke_kdf_t kdf,
-                                    const gnutls_hpke_aead_t aead)
-{
-       const uint8_t psk[32] = { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
-                                 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
-                                 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
-                                 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c,
-                                 0x1d, 0x1e, 0x1f, 0x20 };
-
-       const uint8_t psk2[32] = { 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
-                                  0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e,
-                                  0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35,
-                                  0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
-                                  0x3d, 0x3e, 0x3f, 0x40 };
-
-       test_hpke_auth_psk_mode_keys_should_match(kem, kdf, aead, psk);
-       test_hpke_auth_psk_mode_keys_should_not_match_if_wrong_pkS_is_used(
-               kem, kdf, aead, psk);
-       test_hpke_auth_psk_mode_keys_should_not_match_if_different_psk_is_used_for_decap(
-               kem, kdf, aead, psk, psk2);
-}
-
-void doit(void)
-{
-       gnutls_global_init();
-
-       const gnutls_hpke_kem_t kems[] = { GNUTLS_HPKE_KEM_DHKEM_X25519,
-                                          GNUTLS_HPKE_KEM_DHKEM_X448,
-                                          GNUTLS_HPKE_KEM_DHKEM_P256,
-                                          GNUTLS_HPKE_KEM_DHKEM_P384,
-                                          GNUTLS_HPKE_KEM_DHKEM_P521 };
-       const size_t num_kems = sizeof(kems) / sizeof(kems[0]);
-
-       const gnutls_hpke_kdf_t kdfs[] = { GNUTLS_HPKE_KDF_HKDF_SHA256,
-                                          GNUTLS_HPKE_KDF_HKDF_SHA384,
-                                          GNUTLS_HPKE_KDF_HKDF_SHA512 };
-       const size_t num_kdfs = sizeof(kdfs) / sizeof(kdfs[0]);
-
-       const gnutls_hpke_aead_t aeads[] = {
-               GNUTLS_HPKE_AEAD_AES_128_GCM, GNUTLS_HPKE_AEAD_AES_256_GCM,
-               GNUTLS_HPKE_AEAD_CHACHA20_POLY1305
-       };
-       const size_t num_aeads = sizeof(aeads) / sizeof(aeads[0]);
-
-       for (size_t i = 0; i < num_kems; i++) {
-               for (size_t j = 0; j < num_kdfs; j++) {
-                       for (size_t k = 0; k < num_aeads; k++) {
-                               suite_hpke_base_mode(kems[i], kdfs[j],
-                                                    aeads[k]);
-                               suite_hpke_psk_mode(kems[i], kdfs[j], aeads[k]);
-                               suite_hpke_auth_mode(kems[i], kdfs[j],
-                                                    aeads[k]);
-                               suite_hpke_auth_psk_mode(kems[i], kdfs[j],
-                                                        aeads[k]);
-                       }
-               }
-       }
-
-       gnutls_global_deinit();
-}