diff --git a/cipher/kem-ecc.c b/cipher/kem-ecc.c
index e2967285..b4be90b6 100644
--- a/cipher/kem-ecc.c
+++ b/cipher/kem-ecc.c
@@ -1,547 +1,553 @@
 /* kem-ecc.c - Key Encapsulation Mechanism with ECC
  * Copyright (C) 2024 g10 Code GmbH
  *
  * This file is part of Libgcrypt.
  *
  * Libgcrypt 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.
  *
  * Libgcrypt 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/>.
  * SPDX-License-Identifier: LGPL-2.1-or-later
  *
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
 #include "g10lib.h"
 #include "cipher.h"
 
 #include "kem-ecc.h"
 
 #define ECC_PUBKEY_LEN_MAX 133
 #define ECC_SECKEY_LEN_MAX 64
 
 static const char *
 algo_to_curve (int algo)
 {
   switch (algo)
     {
     case GCRY_KEM_RAW_X25519:
     case GCRY_KEM_DHKEM25519:
     case GCRY_KEM_OPENPGP_X25519:
     case GCRY_KEM_CMS_X25519_X963_SHA256:
     case GCRY_KEM_CMS_X25519_HKDF_SHA256:
       return "Curve25519";
 
     case GCRY_KEM_RAW_X448:
     case GCRY_KEM_DHKEM448:
       return "X448";
 
     case GCRY_KEM_RAW_BP256:
       return "bp256";
 
     case GCRY_KEM_RAW_BP384:
       return "bp384";
 
+    case GCRY_KEM_RAW_BP512:
+      return "bp512";
+
     default:
       return 0;
     }
 }
 
 
 static int
 algo_to_seckey_len (int algo)
 {
   switch (algo)
     {
     case GCRY_KEM_RAW_X25519:
     case GCRY_KEM_DHKEM25519:
     case GCRY_KEM_OPENPGP_X25519:
     case GCRY_KEM_CMS_X25519_X963_SHA256:
     case GCRY_KEM_CMS_X25519_HKDF_SHA256:
       return 32;
 
     case GCRY_KEM_RAW_X448:
     case GCRY_KEM_DHKEM448:
       return 56;
 
     case GCRY_KEM_RAW_BP256:
       return 32;
 
     case GCRY_KEM_RAW_BP384:
       return 48;
 
+    case GCRY_KEM_RAW_BP512:
+      return 64;
+
     default:
       return 0;
     }
 }
 
 
 static gpg_err_code_t
 ecc_mul_point (int algo, unsigned char *result, size_t result_len,
                const unsigned char *scalar, size_t scalar_len,
                const unsigned char *point, size_t point_len)
 {
   const char *curve = algo_to_curve (algo);
 
   return _gcry_ecc_curve_mul_point (curve, result, result_len,
                                     scalar, scalar_len, point, point_len);
 }
 
 
 gpg_err_code_t
 _gcry_ecc_raw_keypair (int algo, void *pubkey, size_t pubkey_len,
                        void *seckey, size_t seckey_len)
 {
   const char *curve = algo_to_curve (algo);
 
   return _gcry_ecc_curve_keypair (curve,
                                   pubkey, pubkey_len, seckey, seckey_len);
 }
 
 gpg_err_code_t
 _gcry_ecc_raw_encap (int algo, const void *pubkey, size_t pubkey_len,
                      void *ciphertext, size_t ciphertext_len,
                      void *shared, size_t shared_len)
 {
   gpg_err_code_t err;
   unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
   void *pubkey_ephemeral = ciphertext;
   size_t seckey_len;
 
   if (ciphertext_len != pubkey_len)
     return GPG_ERR_INV_VALUE;
 
   seckey_len = algo_to_seckey_len (algo);
   err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, pubkey_len,
                                seckey_ephemeral, seckey_len);
   if (err)
     return err;
 
   /* Do ECDH.  */
   return ecc_mul_point (algo, shared, shared_len, seckey_ephemeral, seckey_len,
                         pubkey, pubkey_len);
 }
 
 gpg_err_code_t
 _gcry_ecc_raw_decap (int algo, const void *seckey, size_t seckey_len,
                      const void *ciphertext, size_t ciphertext_len,
                      void *shared, size_t shared_len)
 {
   /* Do ECDH.  */
   return ecc_mul_point (algo, shared, shared_len, seckey, seckey_len,
                         ciphertext, ciphertext_len);
 }
 
 
 enum
   {
     DHKEM_X25519_HKDF_SHA256 = 0x20, /* Defined in RFC 9180.  */
     DHKEM_X448_HKDF_SHA512   = 0x21
   };
 
 static gpg_err_code_t
 ecc_dhkem_kdf (int kem_algo, size_t ecc_len,
                const unsigned char *ecdh, const unsigned char *ciphertext,
                const unsigned char *pubkey, void *shared)
 {
   gpg_err_code_t err;
   unsigned char *p;
   unsigned char labeled_ikm[7+5+7+ECC_PUBKEY_LEN_MAX];
   int labeled_ikm_size;
   unsigned char labeled_info[2+7+5+13+2*ECC_PUBKEY_LEN_MAX];
   int labeled_info_size;
   gcry_kdf_hd_t hd;
   unsigned long param[1];
   int macalgo;
   int mac_len;
 
   if (kem_algo == DHKEM_X25519_HKDF_SHA256)
     macalgo = GCRY_MAC_HMAC_SHA256;
   else if (kem_algo == DHKEM_X448_HKDF_SHA512)
     macalgo = GCRY_MAC_HMAC_SHA512;
   else
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   mac_len = _gcry_mac_get_algo_maclen (macalgo);
   param[0] = mac_len;
   labeled_ikm_size = 7+5+7+ecc_len;
   labeled_info_size = 2+7+5+13+ecc_len*2;
 
   p = labeled_ikm;
   memcpy (p, "HPKE-v1", 7);
   p += 7;
   memcpy (p, "KEM", 3);
   p[3] = 0;
   p[4] = kem_algo;
   p += 5;
   memcpy (p, "eae_prk", 7);
   p += 7;
   memcpy (p, ecdh, ecc_len);
 
   p = labeled_info;
   /* length */
   p[0] = 0;
   p[1] = mac_len;
   p += 2;
   memcpy (p, "HPKE-v1", 7);
   p += 7;
   memcpy (p, "KEM", 3);
   p[3] = 0;
   p[4] = kem_algo;
   p += 5;
   memcpy (p, "shared_secret", 13);
   p += 13;
   /* kem_context */
   memcpy (p, ciphertext, ecc_len);
   p += ecc_len;
   memcpy (p, pubkey, ecc_len);
   p += ecc_len;
 
   err = _gcry_kdf_open (&hd, GCRY_KDF_HKDF, macalgo, param, 1,
                         labeled_ikm, labeled_ikm_size,
                         NULL, 0, NULL, 0, labeled_info, labeled_info_size);
   if (err)
     return err;
 
   err = _gcry_kdf_compute (hd, NULL);
   if (!err)
     err = _gcry_kdf_final (hd, mac_len, shared);
   _gcry_kdf_close (hd);
   return err;
 }
 
 
 gpg_err_code_t
 _gcry_ecc_dhkem_encap (int algo, const void *pubkey, void *ciphertext,
                        void *shared)
 {
   gpg_err_code_t err;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
   void *pubkey_ephemeral = ciphertext;
   int curveid;
   int kem_algo;
   size_t ecc_len;
 
   if (algo == GCRY_KEM_DHKEM25519)
     {
       curveid = GCRY_ECC_CURVE25519;
       kem_algo = DHKEM_X25519_HKDF_SHA256;
     }
   else if (algo == GCRY_KEM_DHKEM448)
     {
       curveid = GCRY_ECC_CURVE448;
       kem_algo = DHKEM_X448_HKDF_SHA512;
     }
   else
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
 
   err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, ecc_len,
                                seckey_ephemeral, ecc_len);
   if (err)
     return err;
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey_ephemeral, ecc_len,
                        pubkey, ecc_len);
   if (err)
     return err;
 
   return ecc_dhkem_kdf (kem_algo, ecc_len, ecdh, ciphertext, pubkey, shared);
 }
 
 gpg_err_code_t
 _gcry_ecc_dhkem_decap (int algo, const void *seckey, const void *ciphertext,
                        void *shared, const void *optional)
 {
   gpg_err_code_t err;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   unsigned char pubkey_computed[ECC_SECKEY_LEN_MAX];
   const unsigned char *pubkey;
   int curveid;
   int kem_algo;
   size_t ecc_len;
 
   if (algo == GCRY_KEM_DHKEM25519)
     {
       curveid = GCRY_ECC_CURVE25519;
       kem_algo = DHKEM_X25519_HKDF_SHA256;
     }
   else if (algo == GCRY_KEM_DHKEM448)
     {
       curveid = GCRY_ECC_CURVE448;
       kem_algo = DHKEM_X448_HKDF_SHA512;
     }
   else
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
 
   if (optional)
     pubkey = optional;
   else
     {
       err = ecc_mul_point (algo, pubkey_computed, ecc_len, seckey, ecc_len,
                            NULL, ecc_len);
       if (err)
         return err;
 
       pubkey = pubkey_computed;
     }
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey, ecc_len,
                        ciphertext, ecc_len);
   if (err)
     return err;
 
   return ecc_dhkem_kdf (kem_algo, ecc_len, ecdh, ciphertext, pubkey, shared);
 }
 
 
 static gpg_err_code_t
 openpgp_kem_kdf (const unsigned char *ecdh, size_t ecdh_len,
                  const unsigned char *kdf_param, void *shared)
 {
   gpg_err_code_t err;
   gcry_kdf_hd_t hd;
   unsigned long param[1];
   int curve_oid_len;
   int hash_id;
   int kek_id;
   size_t z_len;
   size_t kdf_param_len;
 
   if (kdf_param == NULL)
     return GPG_ERR_INV_VALUE;
 
   curve_oid_len = kdf_param[0];
   hash_id = kdf_param[1+curve_oid_len+3];
   kek_id = kdf_param[1+curve_oid_len+4];
   kdf_param_len = 1+curve_oid_len+5+20+20;
 
   err = _gcry_cipher_algo_info (kek_id, GCRYCTL_GET_KEYLEN, NULL, &z_len);
   if (err)
     return err;
 
   param[0] = z_len;
 
   err = _gcry_kdf_open (&hd, GCRY_KDF_ONESTEP_KDF, hash_id, param, 1,
                         ecdh, ecdh_len,
                         NULL, 0, NULL, 0, kdf_param, kdf_param_len);
   if (err)
     return err;
 
   err = _gcry_kdf_compute (hd, NULL);
   if (!err)
     err = _gcry_kdf_final (hd, z_len, shared);
   _gcry_kdf_close (hd);
 
   return err;
 }
 
 /* In OpenPGP v4, 0x40 is prepended to the native encoding of public
    key.  Here, PUBKEY and CIPHERTEXT are native representation sans
    the prefix.  */
 gpg_err_code_t
 _gcry_openpgp_kem_encap (int algo, const void *pubkey, void *ciphertext,
                          void *shared, const void *optional)
 {
   gpg_err_code_t err;
   int curveid;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   const unsigned char *kdf_param = optional;
   unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
   void *pubkey_ephemeral = ciphertext;
   size_t ecc_len;
 
   if (algo != GCRY_KEM_OPENPGP_X25519)
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   /* From here, it's only for the OpenPGP KEM(Curve25519, One-Step KDF).  */
   curveid = GCRY_ECC_CURVE25519;
 
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
   err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, ecc_len,
                                seckey_ephemeral, ecc_len);
   if (err)
     return err;
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey_ephemeral, ecc_len,
                        pubkey, ecc_len);
   if (err)
     return err;
 
   return openpgp_kem_kdf (ecdh, ecc_len, kdf_param, shared);
 }
 
 /* In OpenPGP v4, secret key is represented with big-endian MPI.
    Here, SECKEY is native fixed size little-endian representation.
    CIPHERTEXT is native representation sans the prefix 0x40.
   */
 gpg_err_code_t
 _gcry_openpgp_kem_decap (int algo, const void *seckey, const void *ciphertext,
                          void *shared, const void *optional)
 {
   gpg_err_code_t err;
   int curveid;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   const unsigned char *kdf_param = optional;
   size_t ecc_len;
 
   if (algo != GCRY_KEM_OPENPGP_X25519)
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   /* From here, it's only for the OpenPGP KEM(Curve25519, One-Step KDF).  */
   curveid = GCRY_ECC_CURVE25519;
 
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey, ecc_len,
                        ciphertext, ecc_len);
   if (err)
     return err;
 
   return openpgp_kem_kdf (ecdh, ecc_len, kdf_param, shared);
 }
 
 
 static gpg_err_code_t
 cms_kem_kdf (int kdf_id, int hash_id,
              const unsigned char *ecdh, size_t ecdh_len,
              const unsigned char *sharedinfo, void *shared)
 {
   gpg_err_code_t err;
   gcry_kdf_hd_t hd;
   unsigned long param[1];
   unsigned int sharedinfolen;
   const unsigned char *supppubinfo;
   unsigned int keylen;
 
   /*
    *  ECC-CMS-SharedInfo ::= SEQUENCE {
    *      keyInfo         AlgorithmIdentifier,
    *      entityUInfo [0] EXPLICIT OCTET STRING OPTIONAL,
    *      suppPubInfo [2] EXPLICIT OCTET STRING  }
    */
   if (!sharedinfo)
     return GPG_ERR_INV_VALUE;
 
   if (sharedinfo[0] != 0x30 /* Constructed | SEQUENCE */
       || sharedinfo[1] >= 0x80)
     return GPG_ERR_INV_VALUE;
 
   sharedinfolen = sharedinfo[1] + 2;
 
   /* Extract KEYLEN for keywrap from suppPubInfo.  */
   supppubinfo = sharedinfo + sharedinfolen - 8;
   if (supppubinfo[0] != 0xA2 /* CLASS_CONTEXT | Constructed | 2 */
       || supppubinfo[1] != 6
       || supppubinfo[2] != 0x04 /* OCTET STRING */
       || supppubinfo[3] != 4)
     return GPG_ERR_INV_VALUE;
 
   keylen = ((((supppubinfo[4] << 24) | (supppubinfo[5] << 16)
               | (supppubinfo[6] << 8) | supppubinfo[7])) + 7) / 8;
 
   param[0] = keylen;
 
   err = _gcry_kdf_open (&hd, kdf_id, hash_id, param, 1,
                         ecdh, ecdh_len,
                         NULL, 0, NULL, 0, sharedinfo, sharedinfolen);
   if (err)
     return err;
 
   err = _gcry_kdf_compute (hd, NULL);
   if (!err)
     err = _gcry_kdf_final (hd, keylen, shared);
   _gcry_kdf_close (hd);
 
   return err;
 }
 
 gpg_err_code_t
 _gcry_cms_kem_encap (int algo, const void *pubkey, void *ciphertext,
                      void *shared, const void *optional)
 {
   gpg_err_code_t err;
   int curveid;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   const unsigned char *sharedinfo = optional;
   unsigned char seckey_ephemeral[ECC_SECKEY_LEN_MAX];
   void *pubkey_ephemeral = ciphertext;
   int kdf_method;
   size_t ecc_len;
 
   if (algo == GCRY_KEM_CMS_X25519_X963_SHA256)
     kdf_method = GCRY_KDF_X963_KDF;
   else if (algo == GCRY_KEM_CMS_X25519_HKDF_SHA256)
     kdf_method = GCRY_KDF_HKDF;
   else
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   curveid = GCRY_ECC_CURVE25519;
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
 
   err = _gcry_ecc_raw_keypair (algo, pubkey_ephemeral, ecc_len,
                                seckey_ephemeral, ecc_len);
   if (err)
     return err;
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey_ephemeral, ecc_len,
                        pubkey, ecc_len);
   if (err)
     return err;
 
   return cms_kem_kdf (kdf_method, GCRY_MD_SHA256, ecdh, ecc_len,
                       sharedinfo, shared);
 }
 
 gpg_err_code_t
 _gcry_cms_kem_decap (int algo, const void *seckey, const void *ciphertext,
                      void *shared, const void *optional)
 {
   gpg_err_code_t err;
   int curveid;
   unsigned char ecdh[ECC_PUBKEY_LEN_MAX];
   const unsigned char *sharedinfo = optional;
   int kdf_method;
   size_t ecc_len;
 
   if (algo == GCRY_KEM_CMS_X25519_X963_SHA256)
     kdf_method = GCRY_KDF_X963_KDF;
   else if (algo == GCRY_KEM_CMS_X25519_HKDF_SHA256)
     kdf_method = GCRY_KDF_HKDF;
   else
     return GPG_ERR_UNKNOWN_ALGORITHM;
 
   curveid = GCRY_ECC_CURVE25519;
   ecc_len = _gcry_ecc_get_algo_keylen (curveid);
 
   /* Do ECDH.  */
   err = ecc_mul_point (algo, ecdh, ecc_len, seckey, ecc_len,
                        ciphertext, ecc_len);
   if (err)
     return err;
 
   return cms_kem_kdf (kdf_method, GCRY_MD_SHA256, ecdh, ecc_len,
                       sharedinfo, shared);
 }
diff --git a/cipher/kem.c b/cipher/kem.c
index ab24c8a9..cbe2699a 100644
--- a/cipher/kem.c
+++ b/cipher/kem.c
@@ -1,441 +1,444 @@
 /* kem.c  - Key Encapsulation Mechanisms
  * Copyright (C) 2023 Simon Josefsson <simon@josefsson.org>
  * Copyright (C) 2023 g10 Code GmbH
  *
  * This file is part of Libgcrypt.
  *
  * Libgcrypt 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.
  *
  * Libgcrypt 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/>.
  * SPDX-License-Identifier: LGPL-2.1-or-later
  */
 
 #include <config.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
 #include "g10lib.h"
 #include "cipher.h"
 #include "sntrup761.h"
 #include "mceliece6688128f.h"
 #include "kyber.h"
 #include "kem-ecc.h"
 
 
 /* Information about the the KEM algoithms for use by the s-expression
  * interface.  */
 static const struct
 {
   const char *name;           /* Name of the algo.  */
   unsigned int namelen;       /* Only here to avoid strlen calls.  */
   int algo;                   /* KEM algo number.   */
   unsigned int nbits;         /* Number of bits.    */
   unsigned int fips:1;        /* True if this is a FIPS140-3 approved KEM. */
   int pubkey_len;             /* Length of the public key.  */
   int seckey_len;             /* Length of the secret key.  */
 } kem_infos[] =
   {
     { "sntrup761", 9, GCRY_KEM_SNTRUP761,  761, 0,
       GCRY_KEM_SNTRUP761_PUBKEY_LEN, GCRY_KEM_SNTRUP761_SECKEY_LEN },
     { "kyber512",  8, GCRY_KEM_MLKEM512,   512, 0,
       GCRY_KEM_MLKEM512_PUBKEY_LEN,  GCRY_KEM_MLKEM512_SECKEY_LEN },
     { "kyber768",  8, GCRY_KEM_MLKEM768,   768, 1,
       GCRY_KEM_MLKEM768_PUBKEY_LEN,  GCRY_KEM_MLKEM768_SECKEY_LEN },
     { "kyber1024", 9, GCRY_KEM_MLKEM1024, 1024, 1,
       GCRY_KEM_MLKEM1024_PUBKEY_LEN, GCRY_KEM_MLKEM1024_SECKEY_LEN },
     { NULL }
   };
 
 /* This is a short version of kem_infos from above.  It is required
  * for the algoithm module interface.  Keep in sync.  */
 static const char *kem_names[] =
   {
     "sntrup761",
     "kyber512",
     "kyber768",
     "kyber1024",
     NULL
   };
 
 
 
 
 /* Helper for sntrup761.  */
 static void
 sntrup761_random (void *ctx, size_t length, uint8_t *dst)
 {
   (void)ctx;
 
   _gcry_randomize (dst, length, GCRY_STRONG_RANDOM);
 }
 
 
 gcry_err_code_t
 _gcry_kem_keypair (int algo,
                    void *pubkey, size_t pubkey_len,
                    void *seckey, size_t seckey_len)
 {
   switch (algo)
     {
     case GCRY_KEM_SNTRUP761:
       if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN
           || pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN)
         return GPG_ERR_INV_ARG;
       sntrup761_keypair (pubkey, seckey, NULL, sntrup761_random);
       return 0;
 
     case GCRY_KEM_CM6688128F:
       mceliece6688128f_keypair (pubkey, seckey);
       return 0;
 
     case GCRY_KEM_MLKEM512:
       if (seckey_len != GCRY_KEM_MLKEM512_SECKEY_LEN
           || pubkey_len != GCRY_KEM_MLKEM512_PUBKEY_LEN)
         return GPG_ERR_INV_ARG;
       kyber_keypair (algo, pubkey, seckey);
       return 0;
 
     case GCRY_KEM_MLKEM768:
       if (seckey_len != GCRY_KEM_MLKEM768_SECKEY_LEN
           || pubkey_len != GCRY_KEM_MLKEM768_PUBKEY_LEN)
         return GPG_ERR_INV_ARG;
       kyber_keypair (algo, pubkey, seckey);
       return 0;
 
     case GCRY_KEM_MLKEM1024:
       if (seckey_len != GCRY_KEM_MLKEM1024_SECKEY_LEN
           || pubkey_len != GCRY_KEM_MLKEM1024_PUBKEY_LEN)
         return GPG_ERR_INV_ARG;
       kyber_keypair (algo, pubkey, seckey);
       return 0;
 
     case GCRY_KEM_RAW_X25519:
     case GCRY_KEM_RAW_X448:
     case GCRY_KEM_RAW_BP256:
     case GCRY_KEM_RAW_BP384:
+    case GCRY_KEM_RAW_BP512:
     case GCRY_KEM_DHKEM25519:
     case GCRY_KEM_DHKEM448:
     case GCRY_KEM_OPENPGP_X25519:
     case GCRY_KEM_CMS_X25519_X963_SHA256:
     case GCRY_KEM_CMS_X25519_HKDF_SHA256:
       return _gcry_ecc_raw_keypair (algo, pubkey, pubkey_len,
                                     seckey, seckey_len);
 
     default:
       return GPG_ERR_UNKNOWN_ALGORITHM;
     }
 
   return GPG_ERR_UNKNOWN_ALGORITHM;
 }
 
 
 gcry_err_code_t
 _gcry_kem_encap (int algo,
                  const void *pubkey, size_t pubkey_len,
                  void *ciphertext, size_t ciphertext_len,
                  void *shared, size_t shared_len,
                  const void *optional, size_t optional_len)
 {
   switch (algo)
     {
     case GCRY_KEM_SNTRUP761:
       if (optional != NULL || optional_len != 0)
         return GPG_ERR_INV_VALUE;
       if (pubkey_len != GCRY_KEM_SNTRUP761_PUBKEY_LEN
           || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN
           || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN)
         return GPG_ERR_INV_VALUE;
       sntrup761_enc (ciphertext, shared, pubkey, NULL, sntrup761_random);
       return 0;
 
     case GCRY_KEM_CM6688128F:
       if (optional != NULL)
 	return GPG_ERR_INV_VALUE;
       mceliece6688128f_enc (ciphertext, shared, pubkey);
       return 0;
 
     case GCRY_KEM_MLKEM512:
     case GCRY_KEM_MLKEM768:
     case GCRY_KEM_MLKEM1024:
       if (optional != NULL)
         return GPG_ERR_INV_VALUE;
       kyber_encap (algo, ciphertext, shared, pubkey);
       return 0;
 
     case GCRY_KEM_RAW_X25519:
     case GCRY_KEM_RAW_X448:
     case GCRY_KEM_RAW_BP256:
     case GCRY_KEM_RAW_BP384:
+    case GCRY_KEM_RAW_BP512:
       if (optional != NULL)
         return GPG_ERR_INV_VALUE;
       return _gcry_ecc_raw_encap (algo, pubkey, pubkey_len,
                                   ciphertext, ciphertext_len,
                                   shared, shared_len);
 
     case GCRY_KEM_DHKEM25519:
     case GCRY_KEM_DHKEM448:
       if (optional != NULL)
         return GPG_ERR_INV_VALUE;
       return _gcry_ecc_dhkem_encap (algo, pubkey, ciphertext, shared);
 
     case GCRY_KEM_OPENPGP_X25519:
       return _gcry_openpgp_kem_encap (algo, pubkey, ciphertext, shared,
                                       optional);
     case GCRY_KEM_CMS_X25519_X963_SHA256:
     case GCRY_KEM_CMS_X25519_HKDF_SHA256:
       return _gcry_cms_kem_encap (algo, pubkey, ciphertext, shared,
                                   optional);
 
     default:
       return GPG_ERR_UNKNOWN_ALGORITHM;
     }
   return GPG_ERR_UNKNOWN_ALGORITHM;
 }
 
 
 gcry_err_code_t
 _gcry_kem_decap (int algo,
                  const void *seckey, size_t seckey_len,
                  const void *ciphertext, size_t ciphertext_len,
                  void *shared, size_t shared_len,
                  const void *optional, size_t optional_len)
 {
   switch (algo)
     {
     case GCRY_KEM_SNTRUP761:
       if (optional != NULL || optional_len != 0)
         return GPG_ERR_INV_VALUE;
       if (seckey_len != GCRY_KEM_SNTRUP761_SECKEY_LEN
           || ciphertext_len != GCRY_KEM_SNTRUP761_ENCAPS_LEN
           || shared_len != GCRY_KEM_SNTRUP761_SHARED_LEN)
         return GPG_ERR_INV_VALUE;
       sntrup761_dec (shared, ciphertext, seckey);
       return 0;
 
     case GCRY_KEM_CM6688128F:
       if (optional != NULL)
 	return GPG_ERR_INV_VALUE;
       mceliece6688128f_dec (shared, ciphertext, seckey);
       return 0;
 
     case GCRY_KEM_MLKEM512:
     case GCRY_KEM_MLKEM768:
     case GCRY_KEM_MLKEM1024:
       if (optional != NULL)
         return GPG_ERR_INV_VALUE;
       kyber_decap (algo, shared, ciphertext, seckey);
       return 0;
 
     case GCRY_KEM_RAW_X25519:
     case GCRY_KEM_RAW_X448:
     case GCRY_KEM_RAW_BP256:
     case GCRY_KEM_RAW_BP384:
+    case GCRY_KEM_RAW_BP512:
       if (optional != NULL)
         return GPG_ERR_INV_VALUE;
       return _gcry_ecc_raw_decap (algo, seckey, seckey_len,
                                   ciphertext, ciphertext_len,
                                   shared, shared_len);
 
     case GCRY_KEM_DHKEM25519:
     case GCRY_KEM_DHKEM448:
       return _gcry_ecc_dhkem_decap (algo, seckey, ciphertext, shared,
                                     optional);
 
     case GCRY_KEM_OPENPGP_X25519:
       return _gcry_openpgp_kem_decap (algo, seckey, ciphertext, shared,
                                       optional);
     case GCRY_KEM_CMS_X25519_X963_SHA256:
     case GCRY_KEM_CMS_X25519_HKDF_SHA256:
       return _gcry_cms_kem_decap (algo, seckey, ciphertext, shared,
                                   optional);
     default:
       return GPG_ERR_UNKNOWN_ALGORITHM;
     }
   return GPG_ERR_UNKNOWN_ALGORITHM;
 }
 
 
 
 /* Generate a KEM keypair using the s-expression interface.  The
  * GENPARAMS is prety simple in this case because it has only the
  * algorithm name.  For example:
  *   (kyber768)
  */
 static gcry_err_code_t
 kem_generate (const gcry_sexp_t genparms, gcry_sexp_t *r_skey)
 {
   gpg_err_code_t ec;
   const char *algo;
   size_t algolen;
   const char *name;
   int i;
   int algoid;
   void *pubkey = NULL;
   void *seckey = NULL;
   size_t pubkey_len, seckey_len;
 
   algo = sexp_nth_data (genparms, 0, &algolen);
   if (!algo || !algolen)
     return GPG_ERR_PUBKEY_ALGO;
   for (i=0; (name=kem_infos[i].name); i++)
     if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen))
       break;
   if (!name)
     return GPG_ERR_WRONG_PUBKEY_ALGO;
   algoid = kem_infos[i].algo;
   pubkey_len = kem_infos[i].pubkey_len;
   seckey_len = kem_infos[i].seckey_len;
   /* (from here on we can jump to leave for cleanup)  */
 
   /* Allocate buffers for the created key.  */
   seckey = xtrycalloc_secure (1, seckey_len);
   if (!seckey)
     {
       ec = gpg_err_code_from_syserror ();
       goto leave;
     }
   pubkey = xtrycalloc (1, pubkey_len);
   if (!pubkey)
     {
       ec = gpg_err_code_from_syserror ();
       goto leave;
     }
 
   /* Generate key.  */
   ec = _gcry_kem_keypair (algoid, pubkey, pubkey_len, seckey, seckey_len);
   if (ec)
     goto leave;
 
   /* Put the key into an s-expression.  */
   ec = sexp_build (r_skey, NULL,
                    "(key-data"
                    " (public-key"
                    "  (%s(p%b)))"
                    " (private-key"
                    "  (%s(p%b)(s%b))))",
                    name,
                    (int)pubkey_len, pubkey,
                    name,
                    (int)pubkey_len, pubkey,
                    (int)seckey_len, seckey);
 
 
   /* FIXME: Add FIPS selftest.  */
 
  leave:
   if (seckey)
     {
       wipememory (seckey, seckey_len);
       xfree (seckey);
     }
   xfree (pubkey);
   return ec;
 }
 
 
 /* Compute a keygrip.  MD is the hash context which we are going to
  * update.  KEYPARAM is an S-expression with the key parameters, this
  * is usually a public key but may also be a secret key.  An example
  * of such an S-expression is:
  *
  *     (kyber768
  *       (p #4243...#)
  *       (s #1718...#))
  *
  * What we hash is the algorithm name, \x00 and the value of p.
  * Including the algorithm name allows us to see a different key
  * despite that it uses the same parameters.  Whether this is a good
  * decision is not clear - but it should not harm.
  */
 static gpg_err_code_t
 kem_compute_keygrip (gcry_md_hd_t md, gcry_sexp_t keyparam)
 {
   gcry_sexp_t l1;
   const char *algo, *data;
   size_t algolen, datalen;
   const char *name;
   int i;
 
   algo = sexp_nth_data (keyparam, 0, &algolen);
   if (!algo || !algolen)
     return GPG_ERR_PUBKEY_ALGO;
   for (i=0; (name=kem_infos[i].name); i++)
     if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen))
       break;
   if (!name)
     return GPG_ERR_WRONG_PUBKEY_ALGO;
 
   _gcry_md_write (md, name, algolen+1); /* (also hash the nul) */
 
   l1 = sexp_find_token (keyparam, "p", 1);
   if (!l1)
     return GPG_ERR_NO_OBJ;
 
   data = sexp_nth_data (l1, 1, &datalen);
   if (!data)
     {
       sexp_release (l1);
       return GPG_ERR_NO_OBJ;
     }
 
   _gcry_md_write (md, data, datalen);
   sexp_release (l1);
 
   return 0;
 }
 
 
 /* Return the number of bits for the key described by PARMS.  On error
  * 0 is returned. */
 static unsigned int
 kem_get_nbits (gcry_sexp_t keyparam)
 {
   const char *algo;
   size_t algolen;
   const char *name;
   int i;
 
   algo = sexp_nth_data (keyparam, 0, &algolen);
   if (!algo || !algolen)
     return 0;  /* GPG_ERR_PUBKEY_ALGO */
   for (i=0; (name=kem_infos[i].name); i++)
     if (kem_infos[i].namelen == algolen && !memcmp (name, algo, algolen))
       break;
   if (!name)
     return 0;  /* GPG_ERR_WRONG_PUBKEY_ALGO */
 
   return kem_infos[i].nbits;
 }
 
 
 /* Generic structure to represent some KEM algorithms in our public
  * key system.  */
 gcry_pk_spec_t _gcry_pubkey_spec_kem =
   {
     GCRY_PK_KEM, { 0, 0 },
     GCRY_PK_USAGE_ENCR,
     "KEM", kem_names,
     "p", "s", "k", "", "p",
     kem_generate,
     NULL,  /* kem_check_secret_key */
     NULL,  /* encrypt_raw - Use gcry_kem_encap instead.  */
     NULL,  /* decrypt_raw - Use gcry_kem_decap unstead.  */
     NULL,  /* sign */
     NULL,  /* verify */
     kem_get_nbits,
     NULL,  /* selftests */
     kem_compute_keygrip,
     NULL,  /* get_curve */
     NULL   /* get_curve_param */
   };