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 */
};