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 . * SPDX-License-Identifier: LGPL-2.1-or-later * */ #include #include #include #include #include #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 * 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 . * SPDX-License-Identifier: LGPL-2.1-or-later */ #include #include #include #include #include #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 */ };